/* eslint-disable max-lines */
/* globals tryTests */
import sinon from 'sinon';
import * as stream from '@openpgp/web-stream-tools';
import { use as chaiUse, expect } from 'chai';
import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import
chaiUse(chaiAsPromised);

import openpgp from '../initOpenpgp.js';
import * as crypto from '../../src/crypto';
import util from '../../src/util.js';
import keyIDType from '../../src/type/keyid.js';
import { getPreferredCipherSuite } from '../../src/key';

import * as input from './testInputs.js';

const detectBrowser = () => typeof navigator === 'object';

const pub_key = [
  '-----BEGIN PGP PUBLIC KEY BLOCK-----',
  'Version: GnuPG v2.0.19 (GNU/Linux)',
  '',
  'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
  'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
  'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
  'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
  'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
  'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
  'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
  '9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa',
  'JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag',
  'Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr',
  'woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb',
  'LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA',
  'SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP',
  'GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2',
  'bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X',
  'W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD',
  'AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY',
  'hz3tYjKhoFTKEIq3y3Pp',
  '=h/aX',
  '-----END PGP PUBLIC KEY BLOCK-----'
].join('\n');

const priv_key = [
  '-----BEGIN PGP PRIVATE KEY BLOCK-----',
  'Version: GnuPG v2.0.19 (GNU/Linux)',
  '',
  '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',
  '=lw5e',
  '-----END PGP PRIVATE KEY BLOCK-----'
].join('\n');

const pub_key_de = [
  '-----BEGIN PGP PUBLIC KEY BLOCK-----',
  'Version: GnuPG v2.0.22 (GNU/Linux)',
  '',
  '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',
  'og2umGfGng==',
  '=v3+L',
  '-----END PGP PUBLIC KEY BLOCK-----'
].join('\n');

const priv_key_de = [
  '-----BEGIN PGP PRIVATE KEY BLOCK-----',
  'Version: GnuPG v2.0.22 (GNU/Linux)',
  '',
  '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',
  'DHTORVqAOdoOcu5Qh7AQg1GdSmfFAsx2',
  '=kyeP',
  '-----END PGP PRIVATE KEY BLOCK-----'
].join('\n');

const priv_key_2000_2008 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
t/ia1kMpSEiOVLlX5dfHZzhR3WNtBqU=
=C0fJ
-----END PGP PRIVATE KEY BLOCK-----`;

const priv_key_2038_2045 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
0uyeibPdgJTjARMuQzDFA8bdbM540vBf5Q==
=WLIN
-----END PGP PRIVATE KEY BLOCK-----`;

const priv_key_expires_1337 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xcA4BAAAAAEBAgCgONc0J8rfO6cJw5YTP38x1ze2tAYIO7EcmRCNYwMkXngb
0Qdzg34Q5RW0rNiR56VB6KElPUhePRPVklLFiIvHABEBAAEAAf9qabYMzsz/
/LeRVZSsTgTljmJTdzd2ambUbpi+vt8MXJsbaWh71vjoLMWSXajaKSPDjVU5
waFNt9kLqwGGGLqpAQD5ZdMH2XzTq6GU9Ka69iZs6Pbnzwdz59Vc3i8hXlUj
zQEApHargCTsrtvSrm+hK/pN51/BHAy9lxCAw9f2etx+AeMA/RGrijkFZtYt
jeWdv/usXL3mgHvEcJv63N5zcEvDX5X4W1bND3Rlc3QxIDxhQGIuY29tPsJ7
BBABCAAvBQIAAAABBQMAAAU5BgsJBwgDAgkQzcF99nGrkAkEFQgKAgMWAgEC
GQECGwMCHgEAABAlAfwPehmLZs+gOhOTTaSslqQ50bl/REjmv42Nyr1ZBlQS
DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ
=/7PI
-----END PGP PRIVATE KEY BLOCK-----`;

const priv_key_sha3_512 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGZN8edBsAAAAgdUMlFMFCVKNo7sdUd6FVBos6NNjpUpSdrodk6BfPb/kA+3bu
A2+WY2LwyxlX5o07WR2VSn+wuegC3v28yO0tClHCtwYfGw4AAABIBYJk3x50BAsJ
CAcHFQ4MCgkICwIWAAIXgAKbAwIeCSIhBpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3W
pW1U6svHvCUiBScJAgcCAAAAACMZIP8aHixoyC9wS3q/TNV/IfOQa81f+U5Ucz6H
4I+c5bWRYUzH/piBB4n5FoYlld+/SViCQIBCQ+fynLmaj5wlf22+mISTt/9je1Zf
YWlJ+WSJyi5gY5EH9DubfuIU3VaqCM0aQmVybmFkZXR0ZSA8YkBleGFtcGxlLm9y
Zz7CugYTGw4AAABLBYJk3x50BAsJCAcHFQ4MCgkICwIWAAIXgAIZAQKbAwIeCSIh
BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiBScJAgcCAAAAAMMGIJEi
9+yqkFKsNwX1H5II0riPudFpwBx2ypVjNk4aNb7Exl56Aac4tXEhz4fH41q0dAzF
ww2erZaiUqmohQ4AFSw1jN/WOiDfb1DkjT/HJ8vXMGpwWdgFPoqsWzTNhd5VCcdL
BmTfHnQZAAAAIAGMcsqVCXLclRhVamWciSxmnYF1FFs80W7dNUH07HUOAHh/S601
If+/eZKDIj3jq7oOe2PzHSYEK+mpQD1hBpF2wpsGGBsOAAAALAWCZN8edAKbDCIh
BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiAAAAANj3IBknZTPsMpWA
we0Jl5gw/Dj4lWAGoJfWfk+6s3Q86Hag3Hu8VBsapzmul+vzy0KJa+ZRcZz2n8aj
0vTl4sOZ0EcCdFDfkh/tR//gKkT6BiSBG86WoFq3f6U/RC+z0Ym7Dw==
-----END PGP PRIVATE KEY BLOCK-----`;

const passphrase = 'hello world';
const plaintext = input.createSomeMessage();
const password1 = 'I am a password';
const password2 = 'I am another password';

const twoPasswordGPGFail = [
  '-----BEGIN PGP MESSAGE-----',
  'Version: OpenPGP.js v3.0.0',
  'Comment: https://openpgpjs.org',
  '',
  'wy4ECQMIWjj3WEfWxGpgrfb3vXu0TS9L8UNTBvNZFIjltGjMVkLFD+/afgs5',
  'aXt0wy4ECQMIrFo3TFN5xqtgtB+AaAjBcWJrA4bvIPBpJ38PbMWeF0JQgrqg',
  'j3uehxXy0mUB5i7B61g0ho+YplyFGM0s9XayJCnu40tWmr5LqqsRxuwrhJKR',
  'migslOF/l6Y9F0F9xGIZWGhxp3ugQPjVKjj8fOH7ap14mLm60C8q8AOxiSmL',
  'ubsd/hL7FPZatUYAAZVA0a6hmQ==',
  '=cHCV',
  '-----END PGP MESSAGE-----'
].join('\n');

const ecdh_msg_bad = `-----BEGIN PGP MESSAGE-----
Version: ProtonMail
Comment: https://protonmail.com

wV4DlF328rtCW+wSAQdA9FsAz4rCdoxY/oZaa68WMPMXbO+wtHs4ZXtAOJOs
SlwwDaABXYC2dt0hUS2zRAL3gBGf4udH/CKJ1vPE58sNeh0ERYLxPHgwrpqI
oNVWOWH50kUBIdqd7by8RwLOk9GyV6008iFOlOG90mfjvt2g5DsnSB4wEeMg
pVu3fXj8iAKvFxvihwv1M7gNtP14StP6CngvyGVVEHQ=
=mvcB
-----END PGP MESSAGE-----`;

const ecdh_dec_key = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.4.6
Comment: https://openpgpjs.org

xYYEXEBTPxYJKwYBBAHaRw8BAQdAbXBY+2lpOatB+ZLokS/JIWqrVOseja9S
ewQxMKN6ueT+CQMIuUXr0XofC6VgJvFLyLwDlyyvT4I1HWGKZ6W9HUaslKvS
rw362rbMZKKfUtfjRJvpqiIU3Dr7iDkHB5vT7Tp5S7AZ2tNKoh/bwfTKdHsT
1803InFhX3Rlc3RlcjJAcHJvdG9ubWFpbC5jb20iIDxxYV90ZXN0ZXIyQHBy
b3Rvbm1haWwuY29tPsJ3BBAWCgAfBQJcQFM/BgsJBwgDAgQVCAoCAxYCAQIZ
AQIbAwIeAQAKCRClzcrGJTMHyTpjAQCJZ7p0TJBZyPQ8m64N24glaM6oM78q
2Ogpc0e9LcrPowD6AssY2YfUwJNzVFVzR+Lulzu6XVPjn0pXGMhOl03SrQ3H
iwRcQFM/EgorBgEEAZdVAQUBAQdAAgJJUhKvjGWMq1sDhrJgvqbHK1t1W5RF
Xoet5noIlAADAQgH/gkDCOFdJ7Yv2cTZYETRT5+ak/ntmslcAqtk3ebd7Ok3
tQIjO3TYUbkV1eqrpA4I42kGCUkU4Dy26wxuaLRSsO1u/RgXjExZLP9FlWFI
h6lLS1bCYQQYFggACQUCXEBTPwIbDAAKCRClzcrGJTMHyfNBAP9sdyU3GHNR
7+QdwYvQp7wN+2VUd8vIf7iwAHOK1Cj4ywD+NhzjFfGYESJ68nnkrYlYdf+u
OBqYz6mzZAWQZqsjbg4=
=zrks
-----END PGP PRIVATE KEY BLOCK-----`;

const ecdh_msg_bad_2 = `-----BEGIN PGP MESSAGE-----
Version: ProtonMail
Comment: https://protonmail.com

wV4DtM+8giJEGNISAQhA2rYu8+B41rJi6Gsr4TVeKyDtI0KjhhlLZs891rCG
6X4wxNkxCuTJZax7gQZbDKh2kETK/RH75s9g7H/WV9kZ192NTGmMFiKiautH
c5BGRGxM0sDfAQZb3ZsAUORHKPP7FczMv5aMU2Ko7O2FHc06bMdnZ/ag7GMF
Bdl4EizttNTQ5sNCAdIXUoA8BJLHPgPiglnfTqqx3ynkBNMzfH46oKf08oJ+
6CAQhJdif67/iDX8BRtaKDICBpv3b5anJht7irOBqf9XX13SGkmqKYF3T8eB
W7ZV5EdCTC9KU+1BBPfPEi93F4OHsG/Jo80e5MDN24/wNxC67h7kUQiy3H4s
al+5mSAKcIfZJA4NfPJg9zSoHgfRNGI8Q7ao+c8CLPiefGcMsakNsWUdRyBT
SSLH3z/7AH4GxBvhDEEG3cZwmXzZAJMZmzTa+SrsxZzRpGB/aawyRntOWm8w
6Lq9ntq4S8suj/YK62dJpJxFl8xs+COngpMDvCexX9lYlh/r/y4JRQl06oUK
wv7trvi89TkK3821qHxr7XwI1Ncr2qDJVNlN4W+b6WFyLXnXaJAUMyZ/6inm
RR8BoR2KkEAku3Ne/G5QI51ktNJ7cCodeVOkZj8+iip1/AGyjxZCybq/N8rc
bpOWdMhJ6Hy+JzGNY1qNXcHJPw==
=99Fs
-----END PGP MESSAGE-----`;

const ecdh_dec_key2 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.4.9
Comment: https://openpgpjs.org

xYYEXEg93hYJKwYBBAHaRw8BAQdAeoA+T4vr3P0hFFsbzJpgy7/ZnKCrlehr
Myk5QAsBYgf+CQMIQ76YL5sEx+Zgr7DLZ5fhQn1U9+8aLIQaIbaT51nEjEMD
7h6mrJmp7oIr4PyijsIU+0LasXh/qlNeVQVWSygDq9L4nXDEGQhlMq3oH1FN
NM07InBha292c2thdGVzdEBwcm90b25tYWlsLmNvbSIgPHBha292c2thdGVz
dEBwcm90b25tYWlsLmNvbT7CdwQQFgoAHwUCXEg93gYLCQcIAwIEFQgKAgMW
AgECGQECGwMCHgEACgkQp7+eOYEhwd6x5AD9E0LA62odFFDH76wjEYrPCvOH
cYM56/5ZqZoGPPmbE98BAKCz/SQ90tiCMmlLEDXGX+a1bi6ttozqrnSQigic
DI4Ix4sEXEg93hIKKwYBBAGXVQEFAQEHQPDXy2mDfbMKOpCBZB2Ic5bfoWGV
iXvCFMnTLRWfGHUkAwEIB/4JAwhxMnjHjyALomBWSsoYxxB6rj6JKnWeikyj
yjXZdZqdK5F+0rk4M0l7lF0wt5PhT2uMCLB7aH/mSFN1cz7sBeJl3w2soJsT
ve/fP/8NfzP0wmEEGBYIAAkFAlxIPd4CGwwACgkQp7+eOYEhwd5MWQEAp0E4
QTnEnG8lYXhOqnOw676oV2kEU6tcTj3DdM+cW/sA/jH3FQQjPf+mA/7xqKIv
EQr2Mx42THr260IFYp5E/rIA
=oA0b
-----END PGP PRIVATE KEY BLOCK-----`;

const mismatchingKeyParams = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js v4.7.0
Comment: https://openpgpjs.org

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
0NubenKyljKtyHyoU+S+TqUyx7gf5A==
=Lj9k
-----END PGP PRIVATE KEY BLOCK-----
`;

const rsaPrivateKeyPKCS1 = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=dSNv
-----END PGP PRIVATE KEY BLOCK-----
`;

const gnuDummyKeySigningSubkey = `
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: OpenPGP.js VERSION
Comment: https://openpgpjs.org

xZUEWCC+hwEEALu8GwefswqZLoiKJk1Nd1yKmVWBL1ypV35FN0gCjI1NyyJX
UfQZDdC2h0494OVAM2iqKepqht3tH2DebeFLnc2ivvIFmQJZDnH2/0nFG2gC
rSySWHUjVfbMSpmTaXpit8EX/rjNauGOdbePbezOSsAhW7R9pBdtDjPnq2Zm
vDXXABEBAAH+B2UAR05VAc0JR05VIER1bW15wrgEEwECACIFAlggvocCGwMG
CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEJ3XHFanUJgCeMYD/2zKefpl
clQoBdDPJKCYJm8IhuWuoF8SnHAsbhD+U42Gbm+2EATTPj0jyGPkZzl7a0th
S2rSjQ4JF0Ktgdr9585haknpGwr31t486KxXOY4AEsiBmRyvTbaQegwKaQ+C
/0JQYo/XKpsaX7PMDBB9SNFSa8NkhxYseLaB7gbM8w+Lx8EYBFggvpwBBADF
YeeJwp6MAVwVwXX/eBRKBIft6LC4E9czu8N2AbOW97WjWNtXi3OuM32OwKXq
vSck8Mx8FLOAuvVq41NEboeknhptw7HzoQMB35q8NxA9lvvPd0+Ef+BvaVB6
NmweHttt45LxYxLMdXdGoIt3wn/HBY81HnMqfV/KnggZ+imJ0wARAQABAAP7
BA56WdHzb53HIzYgWZl04H3BJdB4JU6/FJo0yHpjeWRQ46Q7w2WJzjHS6eBB
G+OhGzjAGYK7AUr8wgjqMq6LQHt2f80N/nWLusZ00a4lcMd7rvoHLWwRj80a
RzviOvvhP7kZY1TrhbS+Sl+BWaNIDOxS2maEkxexztt4GEl2dWUCAMoJvyFm
qPVqVx2Yug29vuJsDcr9XwnjrYI8PtszJI8Fr+5rKgWE3GJumheaXaug60dr
mLMXdvT/0lj3sXquqR0CAPoZ1Mn7GaUKjPVJ7CiJ/UjqSurrGhruA5ikhehQ
vUB+v4uIl7ICcX8zfiP+SMhWY9qdkmOvLSSSMcTkguMfe68B/j/qf2en5OHy
6NJgMIjMrBHvrf34f6pxw5p10J6nxjooZQxV0P+9MoTHWsy0r6Er8IOSSTGc
WyWJ8wmSqiq/dZSoJcLAfQQYAQIACQUCWCC+nAIbAgCoCRCd1xxWp1CYAp0g
BBkBAgAGBQJYIL6cAAoJEOYZSGiVA/C9CT4D/2Vq2dKxHmzn/UD1MWSLXUbN
ISd8tvHjoVg52RafdgHFmg9AbE0DW8ifwaai7FkifD0IXiN04nER3MuVhAn1
gtMu03m1AQyX/X39tHz+otpwBn0g57NhFbHFmzKfr/+N+XsDRj4VXn13hhqM
qQR8i1wgiWBUFJbpP5M1BPdH4Qfkcn8D/j8A3QKYGGETa8bNOdVTRU+sThXr
imOfWu58V1yWCmLE1kK66qkqmgRVUefqacF/ieMqNmsAY+zmR9D4fg2wzu/d
nPjJXp1670Vlzg7oT5XVYnfys7x4GLHsbaOSjXToILq+3GwI9UjNjtpobcfm
mNG2ibD6lftLOtDsVSDY8a6a
=KjxQ
-----END PGP PRIVATE KEY BLOCK-----
`;

const multipleEncryptionAndSigningSubkeys = `-----BEGIN PGP PRIVATE KEY BLOCK-----

lQHYBGApVbABBADKOR9p2mzWczNRwuGhUDxuO57pUuOotGsFqPMtGVEViYYDckHa
3IGiFdi9+OWGQERtzR7AdwziuCW5X9L8UwcgsvMg5LrxbvK6oYsYOetKcBlFnwB0
yFWzyf9hccoF/ddxQBuwBO90eFWjNRSeONtfi6uay+yH9wVUd9+b6QzqBQARAQAB
AAP7B9n06sa0wBTD8tI2sW0sk3kUH+n8ddHfb95R5rfbapMm1V5rySQTkmf3vNR7
kN1Q6tRyc7WLlgfhSxO53NsaZSxlQwjlwM0j5TfUsCDM08fHezg53VvbTiNzOVjZ
wLBEuLTYMCy5/zEOixpXmuVPREIQqrUwR9zYnNgqAhAJSsECANLJ1rWe8tld6jN9
ab0Aitt53wDNI8hO2PJCSR/fLZ8Yx3vDPHlryPvzntkxE25cPbh0PedfGY+IpJ6E
72T0TmECAPWY+RO29n75iceOA0CbNW737+DYdTJ3PFuM7HJnchlIgA7OkIdsIrPL
fVpb2MWM6KVLtXGBzkWickx3Rj4JViUCAPF52+zlXLvQToxLl7U8AQfPisHQESRR
seX67ow5RTG+MU4tZgwYUBKaXx7T5VJLZWueKN3jAlMkz6XOO1pOcOym6bQhQWxp
IENoZXJyeSA8Y3RwYWxpQGFsaWNoZXJyeS5uZXQ+iM4EEwEIADgWIQR02Pmpv9fW
zWRiQcoTf/zV6HQIsgUCYClVsAIbAQULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAK
CRATf/zV6HQIssCXA/wMuK7pXaPp8635MnohSauIkIYLnznnYT5BZPYyyqoIw92I
PeAcNQObkmxNr4sNQqHwMPL40wZrIlJUFG3W0RD7dTnAJrc7ExSFd3bRU88YHr+y
USQEhf7/czzJRz5x/FAb+0netgSwkrJtP92GjOz8/ZjNW6KxkS1zU2ho0jvtKJ0B
2ARgKVXGAQQAqSjNbJWRrXNdry0x5Swwn0trbOA/GbQ6/xuSrrburj/UirpykzEb
hP0XHVGJoX13bZWNZHtO7J4mwu1tSV4vxE5/OP71wSRd6erH7Gzmj24IxKIWjn3O
wY4W9URQspIhm5xyMevszi3EWU+JDqOdYETbyrd72QzuyZ+2MySqZ7cAEQEAAQAD
/jpRvWTyufs9O27SG5Ihpo+8vkgWldqkRvS9ylfe7LH5gqrjde3mL9EtOoNaoaMh
8QNAXLNoScENE1r/5T42sSuiax1ocapjUx3gLw57jABU4E4pgq5VtAOUq+pEnln4
U/WBS49Q7DwuhF5p7Ey7o+NdPB5U8i02zmHspA3/1yCFAgDBKDafZzfTdx+JALDU
4tmRnwm3FZ+dONzRL2Co72OJHf/YmoAOkRdsLh64Sc5ixh+UCRT0X/cqZKAFtU6T
YIPrAgDgMdqXoQpd9C+tFctg4FVP6VMc5Gqx5rPvyd4lKktCnhppN6BR8I6zfF/I
1j8mNqiU3bSINuih2sNLnDG12BRlAf98DhHi1nYRC7oaX8A67xEMCtTdgY77nftB
YNQrWWlKOsezWHsvnGs/yxMPNliF4H2MsripkFHNku8YvrqPzeooopmJAWsEGAEI
ACAWIQR02Pmpv9fWzWRiQcoTf/zV6HQIsgUCYClVxgIbAgC/CRATf/zV6HQIsrQg
BBkBCAAdFiEE9m+FABC9Jcx9Ae1bZjJ3r2BABjgFAmApVcYACgkQZjJ3r2BABjhG
awP/fdrw+AYAzgDc4xPyZyH3kJmhhcz8BetjgNazjIXe2ny979IHHiI9ZWQxqvY/
wZgdwPQZQupo/aPilNN6aIwuQXNsZvHFF4uTmtEFjE4Qtx3y2B8W/K2XDtXU6EO7
f8ZyNTk2js5pQG25A+C4quxAfjT+z3ilZngIP5IbG78ZiDEuDgP/e4/gec5qSo6c
aQPWOv+fhPBN91AaiRUB2Z1vB5Dbz0uiPIvcD1F0Gul9W0sXX+ZZkq3PSBD/jWoP
v49A+4cNGeCItaLCAZT1IgybQpWtDx60kb3Nna1CzTt8n3lmMl2mIFBDT60WHaDw
3tkZ07yYT38aCnM5IaQYjKBiAAHQQcKdAdgEYClV3AEEALhh40h7Fk/N/+EULzM8
H0fYyoSC2oAEn2MKGs88fa8vqdphAxXJ/z5hvUVJ9mEvjpat3QYsMxTjUed/Hf65
4l2woOMG7QFPoCGAhcUP1FY71SMScWK20WoM6zqcuU5oDsmOFfaP9nTCXfAe/qr5
LaNiY3V+S6po9VFyVljeuO+RABEBAAEAA/oDXb5Nqo7HU2qmuioWr+QUY+bbcpdg
6hIGHQyNCyTdBc7ukAdOM/fxSc06nSwhUqIXpNooY0EQklTH5/zlDjFtu3hy3C68
KBvKF8sj/HizpvuhvR2xnunfcJ5kOc9jwXDZMrv/NxvmbVZCNxfbJ4/K7Jrfe1Uh
RbfL3XEiODxqwQIAzvXjguhFX0fRDIrbzsEUIRGyabqp1uKkl0JbRqVKOznLiQXn
0QGkK8/4hmTDczcjT8xWVinK0bjvqKJ1WY2a0QIA5BJsEYP9nkaTJYbbjfaDDR7e
s89BN19y4HwO+/CwkywbatFDCoyN9bbRcLDsbAANIo94zFP4qmkqsyuR4uG4wQIA
y6ahGLf9DJ7JUhbNkh3r1HSPP8BB9dYhDSdRaC15Fa1Cb9Dj0SFZo+Abg8c+shqS
3lg6XlsoVDkLMVnRZSgl56EniLYEGAEIACAWIQR02Pmpv9fWzWRiQcoTf/zV6HQI
sgUCYClV3AIbDAAKCRATf/zV6HQIshDUA/0cAH5fQEvrs716+ppg5VWoKR1ZCku/
RRm//oOTqYfpU7AxJfBu05PQn26Td5XPll+HXqyMFzl2Xc//9+Nn3p8gYnOLgjYy
8OkQ6o6aVQOLftOn9+NYfaI+pFOHveyK5J3YpHr9VA8QfCA/JkN+Qy6n+HbkUZfx
MwNH6sh9tNWpYJ0B1wRgKVXoAQQA67PwBBU3RLeGWBdkziB96Dyb+bwjCPvvO4Ad
iQWdE2JMMdK81FjHaiW7QWMTiI71ZWrh4F6kU5Vg5X22qtgAddfZh4aXFRZSOL0b
/dfKTVGELqLhL4EY+jDe0B3s9cGdD/OL2NatZ6abR0Gx08Vrk+TUN9RiHcSCwmwY
Sqy/vcUAEQEAAQAD9Ai/JKkCIIrsRJAATj1X91Qm66LY2HP85WPP3Ui4bJvLighP
SbKXmM7Xl5tVkeP/ahvZW4h3+qEfafkaMS0l1t52aMkGM6n8p6DK7eeWEP8geahL
sLKlomFJ+FFfchCWpkg97cBbHyZd9O8UOfQzzYYL88V7VmSt0SEdo0NUnPMCAPPT
C2rp4G072qKaBzEjZr3sa+GAjjaCgfQ9C2/ZmFczgy9isijPXcub2tkyzTLAhKig
/IwIwSTJN32WSlhXL9sCAPd5EhwGcvFWouMQ20kd7te4hY+WsyawsDMzGcHsn93m
TFKwEYjd4b0tNYyZFfeKBdEPtlLjdyDMLm4MAS9Tit8CALsCQsFvkDSDSFb7dj5R
99nIGYB9jCCMfLH58LmbYh1pOp7pT+QVmR2fZTojZ3CkHel/ctuWEqE/VquRPaaz
r4yjJokBawQYAQgAIBYhBHTY+am/19bNZGJByhN//NXodAiyBQJgKVXoAhsCAL8J
EBN//NXodAiytCAEGQEIAB0WIQQQf5elFAcf8pAyRJ+74USR5u5jZgUCYClV6AAK
CRC74USR5u5jZiM5A/9lTC1mnJPgMG8GhfyGasvBlCQCgwPGBH7NR6TZZJTf5CpN
scKsBHm6zPQolH7qldzDqLD1E6XWC3uEqyrPSTnSL+q9xeDhJHduwNGeKMg4DUvb
dXvd1GLW8Aj10lqCGH2qdSccoBP8JMLrQGk1ep0939593dXHNbsil93w6m0V4rvJ
A/4k1sLqXwjadRThUrTIRSVncHpFS39L0AVPFdXZD4wY39Ft2DnI2Ozjv8S2CYEy
ijwTwHrosgWgbXpG3QCmuZVYCV2rL/uVGdEE8qYH9W0mBmNKSQTCaFtYSYLu9I8P
w+XV36ZRx9jOvIrl1/Fyu2tBcMiOK30wy12aW8sLzR6rbp0B2ARgKVYPAQQAveJM
JdyAibYY9RPJZ41laScjdYJfKptCHSqewudPAoA5cIxt7NbCFOl2cfl0QSreBpTj
7AWaJjCYOweF5grxrZt80wNzHJ/gYT53ygA3nmDtVUBWif8Sx8ZJB6yfuJhxOoWp
tH6d/yPWOZdjTf8s1xfy/encrfP8tG1eUXB05H0AEQEAAQAD/iajPxpvKWqcNqzb
114uW+XPNHxrSGEbkZLswrxnI+Ee5VE9Cfso4fouXU8o0tqV1fLh5hT3ONwvhDJy
v/DE5lMyZEzLFo66nEQPPPwhjeCRc87CHiKBnUIXiVEQ1+jbbPmxuAuB55gozYsd
2XywID1uijpD4rJbMrZ1K8Tug/NBAgDE3gaslBT+z/OYlSZiE4INeluxGbZLA365
LEuKZcsWiX2lWr+Rzu8PB2wzNoxGYI4NykBT/0pn0gEcsgw7mZxdAgD260tRurQG
BUp1xHlHPJMhD0gJrWeZ117X96nsIUP5Lbym1oVQugWVIpQ8EhAP6jFksrtCqo97
SppI3XNl9uahAf4/8SnzEAJiIVKUL+ybbs3lU09Yi6MezTjTVE3f8tnsjc/+Y872
/6WG/OukMx7Hca7DnET5X+XnYvH7NLU3L242oxmItgQYAQgAIBYhBHTY+am/19bN
ZGJByhN//NXodAiyBQJgKVYPAhsMAAoJEBN//NXodAiy3OoD/iaRzB2HO83uwuFF
i9zIiu4VqTJsgjNlO/tW3HXVgyMg5nhR/uZziFIT1XBkUXaL08Qvzxm8/J4uLWVx
l46E184mkWBy+9KSrXH8vJU7cB1yi9ZGQ140bwZe6ku2ZkhMu4usc5Qaci/CLx8g
Bu9AfaHX9qJvH+oL7/0+LXROMYnonQHYBGApVhoBBAC374LGDgr9k3EvjbiJYjXc
A+43eVv5ACtQ0gbNdnlL6SHzJdEfX2n5A5NnEm5iIqZlYt+cFlSBSpP49bRBUiOg
kHU/k0YH9dp3FvTDVqBe+0peUixPGGR3OLfCONIpzzVKsMa+9GDpQUewxF89t+NU
gT85a3RMf5fjJgHXLHQRPQARAQABAAP8CJB24tjpixgP55puMrtnbZijQWL9tNDc
s3UsCuoOyMmQop0qqQ7MxOL1PJHfoOMjI0pgxghGJAUAcdGi9H2qGe4YggnMmGXJ
AxqGdRvrxvnO9XY4dC8/InabIuLEMg/3QZjCthWTlUMCp1fln/7+S8c0mcZcShh+
d+RAyOT91QMCANKWJTSpM8EEWar04SHM53b14evl2ywniSfXCYHEjbdYIMGXnHdF
30pH2MlGyIeUgoeHaoh4Fhrz75wg/gXSPAcCAN+aDDUzO51f9fJu56trJ4SA175+
9nxW9g667ajpC/OC7nPglO/Qw91AU+3CWbQp164ZNbN0TyjnM4fO4fp8P5sCAJz3
nSAMZEiytf4uyyBk+TKIAfQ+6jJcFtujnuWQ/UXXYL75X9h7Lcgr63U4bd4gulFI
tq02YoNmmP6xrxa+qpmreYkBawQYAQgAIBYhBHTY+am/19bNZGJByhN//NXodAiy
BQJgKVYaAhsCAL8JEBN//NXodAiytCAEGQEIAB0WIQQoMsv6M8xnR4iJeb0+DyDx
px1t/QUCYClWGgAKCRA+DyDxpx1t/SbeA/9lxHD91plBvM1HR3EyJsfAzzFiJU4n
JGjmbAj5hO/EdrSwxc0BM32WTvax9R9xV5utu1dMAO/w75DJ+2ervb1arCKg4mSj
utTy6htqquI3tEhyu33HlmO65YPR9Grbh/WPi1qrMdseTGTd5UUNkIB4iRV9T+TX
YLFjy1PmdiGmGglwA/9QkcYF67NWueVSSJ7Jf9T5inF+/9ZMQtSZujYpjRcNy8ab
dDhH74WSqFTmoB4oKAwC5zXbTTp7KjsqlYZ48QVom8A0rJzxruu5keKCGpo20qyG
gUsJ58MHan76ieB0+jv/dn8MBQjLfl6NBvzYLVUxmjTtdLYg3ZYhPz+izshXAZ0B
2ARgKVY8AQQA1Mb4QbDhfWb8Z6rEcy2mddA/ksrfyjynaLhVu8S5+afjnHrJuxmQ
2OqAX2ttNJAXgsw1LgjDMVKe8nhwVV0Vn3HtXTgh5u4hDRlSX5EDpXKXnMk8M5hh
JDgxHEbTOZyRriIbUImESuLnJJPjO3x43RGb1gZNkXS3lwRl5K9MgvEAEQEAAQAD
/AzAIJvVJOoOHBV9QPjy9RztvgWGpTr6AAExPKf8HbXldukHXaPZ4Blzkf5F0n06
HkKPCKfJzCKeRBqdF4QyCAvSNwxSYdNWtA62UZByeEgzCGmAHm7/pZR6NFdc/7Xy
NDNggLPrg/6bEUWED6dI4Y3BNcTydcCRTXAewK2+90XtAgDeFmzMKh68M9IRXUMt
XeA5amwC8/mzQaSdOE9xdE4MVgdAc79x445kSpGu/+vxarGpe9ZYA8FQU8fFjE1i
88FNAgD1RJhcUFJ7+/fRCXKgpXMiWrREoeGYjraWTn+ZWKp7L09r+R5zAd8FyClF
lGW4ZwZhZJzUCLk1pbvGcvTYrHY1Af4gSN+UoCriRfasXJvTYalZnAcLC7H6OyvG
HNnmgW4YBIQidlDDsY8vQTBGlL+DUMbs4TsaPQxiE/l6J9jSw0ngnT+ItgQYAQgA
IBYhBHTY+am/19bNZGJByhN//NXodAiyBQJgKVY8AhsMAAoJEBN//NXodAiyskkD
/iIt9CvkQwzh1gfsghVY9FyYVFtqZ1y09+F9V4Gb0vjYtN6NZ+04A67LklgFejS6
MwVb8Ji3aGDA3yIk+DH/ewkYmmAaSO0a6GdPypp/YLkzUGZYV0MefTbqce93usd+
jPmIGfaAsW5TK9KK/VcbFCZZqWZIg8f+edvtjRhYmNcZ
=PUAJ
-----END PGP PRIVATE KEY BLOCK-----`;

const twoPublicKeys = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2.0.19 (GNU/Linux)

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
PepQmgsu
=w6wd
-----END PGP PUBLIC KEY BLOCK-----`;

const twoPrivateKeys = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYJQe2xYJKwYBBAHaRw8BAQdAjTDKUXTWruoPIdDA5tpTEax/nCIKgmeS
jabWRyMTWoEAAQCM8rs15ex7sQ7T4sBf8jHeKvHiUBoTkhKJVAzsnorHdhGn
zRB0ZXN0IDx0ZXN0QGEuaXQ+wowEEBYKAB0FAmCUHtsECwkHCAMVCAoEFgAC
AQIZAQIbAwIeAQAhCRAQIA5NLDEFChYhBAWs5LsefVu3mjXXaBAgDk0sMQUK
BYcBAMxy3zEZhNtw2nnB9jAlIOSeCUJq/GuarTWQkhAZLFIeAP9400rWrELS
zvNgdct9fctoM21ZByUlkmNdPgYf7fjaAMddBGCUHtsSCisGAQQBl1UBBQEB
B0DdGhv0sVHFzGvDPzTYhNKnUxd68oocIEkt5Ku6ZAD0VAMBCAcAAP9rRNBE
OumQKygox59KL7FjEYXSR8TqI4t3CFlfWW/D8A+gwngEGBYIAAkFAmCUHtsC
GwwAIQkQECAOTSwxBQoWIQQFrOS7Hn1bt5o112gQIA5NLDEFCoPdAQCTy2kg
z3F/iZApy2Sf5SIThnQMsgEr296Fgfvm8YMFCAEA82+TF79snlPbVHSIrdDg
lPMSDEkIcxzIQN0EEo1qlwzFWARglB7iFgkrBgEEAdpHDwEBB0D/kNASbsOD
S9RePgrsUDdY3plKDRLIIvpAIkbr1PoDoAABANEBtAiU2YjVOfHzDgbblSCd
+tPSDaYbAyHmCNMDqsRQD8rNEHRlc3QgPHRlc3RAYS5pdD7CjAQQFgoAHQUC
YJQe4gQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJEIrXtvI38e+rFiEERNKb
HKnqdF8HwqMZite28jfx76trWAEA6YFR+4gMFr3xM/HReS+pYE1SSHIQjHgz
SsU0N93pk5EA/ijuLZfsRf7uD6Yb0rEDIJa3NT7KwIUIUtDpbQLtIrcFx10E
YJQe4hIKKwYBBAGXVQEFAQEHQLfK3MpbSeRa1Ko1NtNDNXOc/sqvEeIjAAKg
V0OWVpsJAwEIBwAA/3Nr3/t32OJi9GFEVEN2/VWes5825aFBPEU6UcBaSgCw
EU/CeAQYFggACQUCYJQe4gIbDAAhCRCK17byN/HvqxYhBETSmxyp6nRfB8Kj
GYrXtvI38e+rSKMBAJaIk9bLz+AN0Ho8pHGP3gEddvLwvioNhdkCJ7CfwWmI
AP9fcXZg/Eo55YB/B5XKLkuzDFwJaTlncrD5jcUgtVXFCg==
=q2yi
-----END PGP PRIVATE KEY BLOCK-----`;

const armoredDummyPrivateKey1 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.11 (GNU/Linux)

lQGqBFERnrMRBADmM0hIfkI3yosjgbWo9v0Lnr3CCE+8KsMszgVS+hBu0XfGraKm
ivcA2aaJimHqVYOP7gEnwFAxHBBpeTJcu5wzCFyJwEYqVeS3nnaIhBPplSF14Duf
i6bB9RV7KxVAg6aunmM2tAutqC+a0y2rDaf7jkJoZ9gWJe2zI+vraD6fiwCgxvHo
3IgULB9RqIqpLoMgXfcjC+cD/1jeJlKRm+n71ryYwT/ECKsspFz7S36z6q3XyS8Q
QfrsUz2p1fbFicvJwIOJ8B20J/N2/nit4P0gBUTUxv3QEa7XCM/56/xrGkyBzscW
AzBoy/AK9K7GN6z13RozuAS60F1xO7MQc6Yi2VU3eASDQEKiyL/Ubf/s/rkZ+sGj
yJizBACtwCbQzA+z9XBZNUat5NPgcZz5Qeh1nwF9Nxnr6pyBv7tkrLh/3gxRGHqG
063dMbUk8pmUcJzBUyRsNiIPDoEUsLjY5zmZZmp/waAhpREsnK29WLCbqLdpUors
c1JJBsObkA1IM8TZY8YUmvsMEvBLCCanuKpclZZXqeRAeOHJ0v4DZQJHTlUBtBZU
ZXN0MiA8dGVzdDJAdGVzdC5jb20+iGIEExECACIFAlERnrMCGwMGCwkIBwMCBhUI
AgkKCwQWAgMBAh4BAheAAAoJEBEnlAPLFp74xc0AoLNZINHe0ytOsNtMCuLvc3Vd
vePUAJ9KX3L5IBqHarsa+aJHX7r796SokZ0BWARREZ6zEAQA2WkxmNbfeMzGUocN
3JEVe0o6rxGt5eGrTSmWisduDP3MURabhUXnf4T8oaeYcbJjkLLxMrJmNq55ln1e
4bSG5mDkh/ryKsV81m3F0DbqO/z/891nRSP5fondFVral4wsMOzBNgs4vVk7V/F2
0MPjR90CIhnVDKPAQbQA+3PjUR8AAwUEALn922AEE+0d7xSMMFpR7ic3Me5QEGnp
cT4ft6oc0UK5kAnvKoksZUc0hpBHjX1w3LTz847/5hRDuuDvwvGMWK8IfsjOF9T7
rK8QtJuBEyJxjoScA/YZP5vX4y0U1reUEa0EdwmVrnZzatMAe2FhlaR9PlHkOcm5
DZwkcExL0dbI/gMDArxZ+5N7kH4zYLtr9glJS/pJ7F0YJqJpNwCbqD8+8DqHD8Uv
MgQ/rtBxBJJOaF+1AjCd123hLgzIkkfdTh8loV9hDXMKeJgmiEkEGBECAAkFAlER
nrMCGwwACgkQESeUA8sWnvhBswCfdXjznvHCc73/6/MhWcv3dbeTT/wAoLyiZg8+
iY3UT9QkV9d0sMgyLkug
=GQsY
-----END PGP PRIVATE KEY BLOCK-----`;

const armoredPublicKey1 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.11 (GNU/Linux)

mQGiBFERlw4RBAD6Bmcf2w1dtUmtCLkdxeqZLArk3vYoQAjdibxA3gXVyur7fsWb
ro0jVbBHqOCtC6jDxE2l52NP9+tTlWeVMaqqNvUE47LSaPq2DGI8Wx1Rj6bF3mTs
obYEwhGbGh/MhJnME9AHODarvk8AZbzo0+k1EwrBWF6dTUBPfqO7rGU2ewCg80WV
x5pt3evj8rRK3jQ8SMKTNRsD/1PhTdxdZTdXARAFzcW1VaaruWW0Rr1+XHKKwDCz
i7HE76SO9qjnQfZCZG75CdQxI0h8GFeN3zsDqmhob2iSz2aJ1krtjM+iZ1FBFd57
OqCV6wmk5IT0RBN12ZzMS19YvzN/ONXHrmTZlKExd9Mh9RKLeVNw+bf6JsKQEzcY
JzFkBACX9X+hDYchO/2hiTwx4iOO9Fhsuh7eIWumB3gt+aUpm1jrSbas/QLTymmk
uZuQVXI4NtnlvzlNgWv4L5s5RU5WqNGG7WSaKNdcrvJZRC2dgbUJt04J5CKrWp6R
aIYal/81Ut1778lU01PEt563TcQnUBlnjU5OR25KhfSeN5CZY7QUVGVzdCA8dGVz
dEB0ZXN0LmNvbT6IYgQTEQIAIgUCURGXDgIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC
HgECF4AACgkQikDlZK/UvLSspgCfcNaOpTg1W2ucR1JwBbBGvaERfuMAnRgt3/rs
EplqEakMckCtikEnpxYe
=b2Ln
-----END PGP PUBLIC KEY BLOCK-----`;

const expiredPublicKeyThroughDirectSignature = `-----BEGIN PGP PUBLIC KEY BLOCK-----

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
Be4ubVrj5KjhX2PVNEJd3XZRzaXZE2aAMQ==
=ZeAz
-----END PGP PUBLIC KEY BLOCK-----`;

const eccPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYaYskRYJKwYBBAHaRw8BAQdAlHT6jzgvcng/qDvb+LH+nA4+AWrMLUYf
aNJIuJRUjXMAAP9llTr5+fNSY78FNnpx53muMtyeDINkeUGGwgqAfxj9lhEV
zRN0ZXN0IDx0ZXN0QHRlc3QuaXQ+wowEEBYKAB0FAmGmLJEECwkHCAMVCAoE
FgACAQIZAQIbAwIeAQAhCRBvJAzR+vGyExYhBCaNeWwMzRW97WhAq28kDNH6
8bITWWkA/0R3zADs94dVo+iSNzrtZaDkbHOMb/yjketYmI0XS8UpAP4hUmKN
QcohP6007t0gaQUcgdwum7PKUoM6BeBG8GaTAsddBGGmLJESCisGAQQBl1UB
BQEBB0CibQAv6tvWCWoe6xlkkZGbLpVWvHwgIPzRVdz4e79DdQMBCAcAAP9T
4SntnkgSUnM39dFoTPIoitrsOcHZbvXPCcvclKgZKBJTwngEGBYIAAkFAmGm
LJECGwwAIQkQbyQM0frxshMWIQQmjXlsDM0Vve1oQKtvJAzR+vGyE5ORAQD+
lfFvJjue+tnuIR+ZubxtpKaJpCOWkAcrkx41NtsLwgD/TAkWh1KDWg0IOcUE
MbVkSnU2Z+vhSmYubDCldNOSVwE=
=bTUQ
-----END PGP PRIVATE KEY BLOCK-----`;

const rsaKeyWithNoFeaturesFromRNP = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
8wENR68u88WrKOGN359vq/DKwd3A
=c2mQ
-----END PGP PRIVATE KEY BLOCK-----`;

function withCompression(tests) {
  const compressionTypes = Object.values(openpgp.enums.compression);

  compressionTypes.forEach(function (compression) {
    const compressionName = openpgp.enums.read(openpgp.enums.compression, compression);
    if (compressionName === 'bzip2') {
      return; // bzip2 compression is not supported.
    }
    const group = `compression - ${compressionName}`;

    describe(group, function() {
      let compressSpy;
      let decompressSpy;

      beforeEach(function () {
        compressSpy = sinon.spy(openpgp.CompressedDataPacket.prototype, 'compress');
        decompressSpy = sinon.spy(openpgp.CompressedDataPacket.prototype, 'decompress');
      });

      afterEach(function () {
        compressSpy.restore();
        decompressSpy.restore();
      });

      tests(
        function(options) {
          options.config = { preferredCompressionAlgorithm: compression };
          return options;
        },
        function() {
          if (compression === openpgp.enums.compression.uncompressed) {
            expect(compressSpy.called).to.be.false;
            expect(decompressSpy.called).to.be.false;
            return;
          }

          expect(compressSpy.called).to.be.true;
          expect(compressSpy.thisValues[0].algorithm).to.equal(compression);
          expect(decompressSpy.called).to.be.true;
          expect(decompressSpy.thisValues[0].algorithm).to.equal(compression);
        }
      );
    });
  });
}

export default () => describe('OpenPGP.js public api tests', function() {
  describe('readKey(s) and readPrivateKey(s) - unit tests', function() {
    it('readKey and readPrivateKey should create equal private keys', async function() {
      const key = await openpgp.readKey({ armoredKey: priv_key });
      const privateKey = await openpgp.readPrivateKey({ armoredKey: priv_key });
      expect(key.isPrivate()).to.be.true;
      expect(privateKey.isPrivate()).to.be.true;
      expect(key.isDecrypted()).to.be.false;
      expect(privateKey.isDecrypted()).to.be.false;
      expect(key.getKeyID().equals(privateKey.getKeyID())).to.be.true;
    });

    it('readPrivateKeys and readKeys should create equal private keys', async function() {
      const keys = await openpgp.readKeys({ armoredKeys: twoPrivateKeys });
      const privateKeys = await openpgp.readPrivateKeys({ armoredKeys: twoPrivateKeys });
      // pairwise comparison
      const zip = (arr1, arr2) => arr1.map((el, i) => [el, arr2[i]]);
      zip(keys, privateKeys).forEach(([key, privateKey]) => {
        expect(key.isPrivate()).to.be.true;
        expect(privateKey.isPrivate()).to.be.true;
        expect(key.isDecrypted()).to.be.true;
        expect(privateKey.isDecrypted()).to.be.true;
        expect(key.getKeyID().equals(privateKey.getKeyID())).to.be.true;
      });
    });

    it('readPrivateKey and readPrivateKeys should have consistent results', async function() {
      const privateAndPublicKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTgbk2JHofY
Ph8FuGsDqgwAAQDYVpZt1w6a+vxgb4M351aCpA2sCfx8kbFg23h8Irtm1xFY
zQ88dGVzdEB0ZXN0LmNvbT7CwBMEEBYKAIUFgmZCN1QDCwkHCZDuyoEpQ/KE
H0UUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3JnzKJ8mXOC
wnEp6lVJ/5+rRzR4UcwlL8EjhOS+rV0T8pAFFQgKDA4EFgACAQIZAQKbAwIe
ARYhBECs3D9sMFn6oOxAqu7KgSlD8oQfAAAPbwD+NjyEHt1CRI+0XmgHdiwZ
JN115IO+M37bOxgBnTbVoF0BAMGECXVQoSRVNy0TYf+AUUPQ6tSZ1zLXszwe
FK3w+CoGxjMEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTg
bk2JHofYPh8FuGsDqgzNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3
VAMLCQcJkO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5w
Z3Bqcy5vcmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoM
DgQWAAIBAhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42
PIQe3UJEj7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh
/4BRQ9Dq1JnXMtezPB4UrfD4KgY=
-----END PGP PRIVATE KEY BLOCK-----`;
      // readPrivateKey should read the first private key encountered in a key block
      const key = await openpgp.readPrivateKey({ armoredKey: privateAndPublicKeyBlock });
      const privateKeys = await openpgp.readPrivateKeys({ armoredKeys: privateAndPublicKeyBlock });
      expect(privateKeys.length).to.equal(1);
      expect(key.isPrivate()).to.be.true;
      expect(privateKeys[0].isPrivate()).to.be.true;
      expect(key.isDecrypted()).to.be.true;
      expect(privateKeys[0].isDecrypted()).to.be.true;
      expect(key.getKeyID().equals(privateKeys[0].getKeyID())).to.be.true;
    });

    it('readKey and readKeys should have consistent results', async function() {
      const publicAndPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xjMEZkI3VBYJKwYBBAHaRw8BAQdA7nW1t5qRdtIYzEVEEhRSDgTgbk2JHofY
Ph8FuGsDqgzNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3VAMLCQcJ
kO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5v
cmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoMDgQWAAIB
AhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42PIQe3UJE
j7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh/4BRQ9Dq
1JnXMtezPB4UrfD4KgbFWARmQjdUFgkrBgEEAdpHDwEBB0DudbW3mpF20hjM
RUQSFFIOBOBuTYkeh9g+HwW4awOqDAABANhWlm3XDpr6/GBvgzfnVoKkDawJ
/HyRsWDbeHwiu2bXEVjNDzx0ZXN0QHRlc3QuY29tPsLAEwQQFgoAhQWCZkI3
VAMLCQcJkO7KgSlD8oQfRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5w
Z3Bqcy5vcmfMonyZc4LCcSnqVUn/n6tHNHhRzCUvwSOE5L6tXRPykAUVCAoM
DgQWAAIBAhkBApsDAh4BFiEEQKzcP2wwWfqg7ECq7sqBKUPyhB8AAA9vAP42
PIQe3UJEj7ReaAd2LBkk3XXkg74zfts7GAGdNtWgXQEAwYQJdVChJFU3LRNh
/4BRQ9Dq1JnXMtezPB4UrfD4KgY=
-----END PGP PRIVATE KEY BLOCK-----`;
      // readKey should read the first key encountered in a key block
      const key = await openpgp.readKey({ armoredKey: publicAndPrivateKeyBlock });
      const keys = await openpgp.readKeys({ armoredKeys: publicAndPrivateKeyBlock });
      expect(keys.length).to.equal(2);
      expect(key.isPrivate()).to.be.false;
      expect(keys[0].isPrivate()).to.be.false;
      expect(keys[1].isPrivate()).to.be.true;
    });

    it('readPrivateKey should throw on armored public key', async function() {
      await expect(openpgp.readPrivateKey({ armoredKey: pub_key })).to.be.rejectedWith(/Armored text not of type private key/);
    });

    it('readPrivateKeys should throw on armored public keys', async function() {
      await expect(openpgp.readPrivateKeys({ armoredKeys: twoPublicKeys })).to.be.rejectedWith(/Armored text not of type private key/);
    });
  });

  describe('generateKey - validate user ids', function() {
    it('should fail for invalid user name', async function() {
      const opt = {
        userIDs: [{ name: {}, email: 'text@example.com' }]
      };
      const test = openpgp.generateKey(opt);
      await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/);
    });

    it('should fail for invalid user email address (missing @)', async function() {
      const opt = {
        userIDs: [{ name: 'Test User', email: 'textexample.com' }]
      };
      const test = openpgp.generateKey(opt);
      await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/);
    });

    it('should fail for string user ID', async function() {
      const opt = {
        userIDs: 'Test User <text@example.com>'
      };
      const test = openpgp.generateKey(opt);
      await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/);
    });

    it('should work for valid single user ID object', function() {
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' }
      };
      return openpgp.generateKey(opt);
    });

    it('should work for array of user ID objects', function() {
      const opt = {
        userIDs: [{ name: 'Test User', email: 'text@example.com' }]
      };
      return openpgp.generateKey(opt);
    });

    it('should work for undefined name', function() {
      const opt = {
        userIDs: { email: 'text@example.com' }
      };
      return openpgp.generateKey(opt);
    });

    it('should work for an undefined email address', function() {
      const opt = {
        userIDs: { name: 'Test User' }
      };
      return openpgp.generateKey(opt);
    });
  });

  describe('generateKey - unit tests', function() {
    it('should still support curve="curve25519" for ECC key type (v4 key)', function() {
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' },
        type: 'ecc',
        curve: 'curve25519',
        format: 'object'
      };
      return openpgp.generateKey(opt).then(({ privateKey: key }) => {
        expect(key).to.exist;
        expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined);
        expect(key.getAlgorithmInfo().algorithm).to.equal('eddsaLegacy');
        expect(key.getAlgorithmInfo().curve).to.equal('ed25519Legacy');
        expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined);
        expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh');
        expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519Legacy');
      });
    });

    it('should have default params set (v4 key)', function() {
      const now = util.normalizeDate(new Date());
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' },
        passphrase: 'secret',
        date: now,
        format: 'object'
      };
      return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) {
        for (const key of [publicKey, privateKey]) {
          expect(key).to.exist;
          expect(key.keyPacket.version).to.equal(4);
          expect(key.users.length).to.equal(1);
          expect(key.users[0].userID.name).to.equal('Test User');
          expect(key.users[0].userID.email).to.equal('text@example.com');
          expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined);
          expect(key.getAlgorithmInfo().curve).to.equal('ed25519Legacy');
          expect(+key.getCreationTime()).to.equal(+now);
          expect(await key.getExpirationTime()).to.equal(Infinity);
          expect(key.subkeys.length).to.equal(1);
          expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined);
          expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal('curve25519Legacy');
          expect(+key.subkeys[0].getCreationTime()).to.equal(+now);
          expect(await key.subkeys[0].getExpirationTime()).to.equal(Infinity);
        }
      });
    });

    it('should have default params set (v6 key)', function() {
      const now = util.normalizeDate(new Date());
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' },
        passphrase: 'secret',
        date: now,
        format: 'object',
        config: { v6Keys: true }
      };
      return openpgp.generateKey(opt).then(async function({ privateKey, publicKey }) {
        for (const key of [publicKey, privateKey]) {
          expect(key).to.exist;
          expect(key.keyPacket.version).to.equal(6);
          expect(key.users.length).to.equal(1);
          expect(key.users[0].userID.name).to.equal('Test User');
          expect(key.users[0].userID.email).to.equal('text@example.com');
          expect(key.getAlgorithmInfo().algorithm).to.equal('ed25519');
          expect(key.getAlgorithmInfo().rsaBits).to.equal(undefined);
          expect(key.getAlgorithmInfo().curve).to.equal(undefined);
          expect(+key.getCreationTime()).to.equal(+now);
          expect(await key.getExpirationTime()).to.equal(Infinity);
          expect(key.subkeys.length).to.equal(1);
          expect(key.subkeys[0].getAlgorithmInfo().algorithm).to.equal('x25519');
          expect(key.subkeys[0].getAlgorithmInfo().rsaBits).to.equal(undefined);
          expect(key.subkeys[0].getAlgorithmInfo().curve).to.equal(undefined);
          expect(+key.subkeys[0].getCreationTime()).to.equal(+now);
          expect(await key.subkeys[0].getExpirationTime()).to.equal(Infinity);
        }
      });
    });

    it('should output keypair with expected format', async function() {
      const opt = {
        userIDs: { name: 'Test User', email: 'text@example.com' }
      };
      const armored = await openpgp.generateKey({ ...opt, format: 'armored' });
      expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false;

      const binary = await openpgp.generateKey({ ...opt, format: 'binary' });
      expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false;

      const { privateKey, publicKey } = await openpgp.generateKey({ ...opt, format: 'object' });
      expect(privateKey.isPrivate()).to.be.true;
      expect(publicKey.isPrivate()).to.be.false;
    });
  });

  describe('reformatKey - unit tests', function() {
    it('should output keypair with expected format', async function() {
      const encryptedKey = await openpgp.readKey({ armoredKey: priv_key });
      const original = await openpgp.decryptKey({
        privateKey: encryptedKey,
        passphrase: passphrase
      });

      const opt = {
        privateKey: original,
        userIDs: { name: 'Test User', email: 'text@example.com' }
      };
      const armored = await openpgp.reformatKey({ ...opt, format: 'armored' });
      expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false;

      const binary = await openpgp.reformatKey({ ...opt, format: 'binary' });
      expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false;

      const { privateKey, publicKey } = await openpgp.reformatKey({ ...opt, format: 'object' });
      expect(privateKey.isPrivate()).to.be.true;
      expect(publicKey.isPrivate()).to.be.false;
    });
  });

  describe('revokeKey - unit tests', function() {
    it('should output key with expected format', async function() {
      const encryptedKey = await openpgp.readKey({ armoredKey: priv_key });
      const key = await openpgp.decryptKey({
        privateKey: encryptedKey,
        passphrase: passphrase
      });

      const armored = await openpgp.revokeKey({ key, format: 'armored' });
      expect((await openpgp.readKey({ armoredKey: armored.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ armoredKey: armored.publicKey })).isPrivate()).to.be.false;

      const binary = await openpgp.revokeKey({ key, format: 'binary' });
      expect((await openpgp.readKey({ binaryKey: binary.privateKey })).isPrivate()).to.be.true;
      expect((await openpgp.readKey({ binaryKey: binary.publicKey })).isPrivate()).to.be.false;

      const { privateKey, publicKey } = await openpgp.revokeKey({ key, format: 'object' });
      expect(privateKey.isPrivate()).to.be.true;
      expect(publicKey.isPrivate()).to.be.false;
    });
  });

  describe('decryptKey - unit tests', function() {
    it('should work for correct passphrase', async function() {
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });
      const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() });
      return openpgp.decryptKey({
        privateKey: privateKey,
        passphrase: passphrase
      }).then(unlocked => {
        expect(unlocked.getKeyID().toHex()).to.equal(privateKey.getKeyID().toHex());
        expect(unlocked.subkeys[0].getKeyID().toHex()).to.equal(privateKey.subkeys[0].getKeyID().toHex());
        expect(unlocked.isDecrypted()).to.be.true;
        expect(unlocked.keyPacket.privateParams).to.not.be.null;
        // original key should be unchanged
        expect(privateKey.isDecrypted()).to.be.false;
        expect(privateKey.keyPacket.privateParams).to.be.null;
        expect(privateKey).to.deep.equal(originalKey);
      });
    });

    it('should work with multiple passphrases', async function() {
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });
      const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() });
      return openpgp.decryptKey({
        privateKey: privateKey,
        passphrase: ['rubbish', passphrase]
      }).then(unlocked => {
        expect(unlocked.getKeyID().toHex()).to.equal(privateKey.getKeyID().toHex());
        expect(unlocked.subkeys[0].getKeyID().toHex()).to.equal(privateKey.subkeys[0].getKeyID().toHex());
        expect(unlocked.isDecrypted()).to.be.true;
        expect(unlocked.keyPacket.privateParams).to.not.be.null;
        // original key should be unchanged
        expect(privateKey.isDecrypted()).to.be.false;
        expect(privateKey.keyPacket.privateParams).to.be.null;
        expect(privateKey).to.deep.equal(originalKey);
      });
    });

    it('should fail for incorrect passphrase', async function() {
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });
      const originalKey = await openpgp.readKey({ armoredKey: privateKey.armor() });
      return openpgp.decryptKey({
        privateKey: privateKey,
        passphrase: 'incorrect'
      }).then(function() {
        throw new Error('Should not decrypt with incorrect passphrase');
      }).catch(function(error) {
        expect(error.message).to.match(/Incorrect key passphrase/);
        // original key should be unchanged
        expect(privateKey.isDecrypted()).to.be.false;
        expect(privateKey.keyPacket.privateParams).to.be.null;
        expect(privateKey).to.deep.equal(originalKey);
      });
    });

    it('should fail for corrupted key', async function() {
      const privateKeyMismatchingParams = await openpgp.readKey({ armoredKey: mismatchingKeyParams });
      const originalKey = await openpgp.readKey({ armoredKey: privateKeyMismatchingParams.armor() });
      return openpgp.decryptKey({
        privateKey: privateKeyMismatchingParams,
        passphrase: 'userpass'
      }).then(function() {
        throw new Error('Should not decrypt corrupted key');
      }).catch(function(error) {
        expect(error.message).to.match(/Key is invalid/);
        expect(privateKeyMismatchingParams.isDecrypted()).to.be.false;
        expect(privateKeyMismatchingParams.keyPacket.privateParams).to.be.null;
        expect(privateKeyMismatchingParams).to.deep.equal(originalKey);
      });
    });

    it('should fail for encrypted key with unknown s2k (unparseableKeyMaterial)', async function() {
      // key encrypted with invalid s2kType = 23, to test that it can still be used for encryption/verification
      const encryptedKeyUnknownS2K = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xYYEZJ2H3RYJKwYBBAHaRw8BAQdA3V39Xv0+436Rpn/2UlcnOC1BGprmAlWY
RBKjAq0hAtD+CRcIdHzwqoLa54cAbBOEIgBh7Xa1Qh5wCGAmEVWnAldaqvk+
NcvUL2bR6AQsGIT6YEihOS3xLKobMOd2XlO5ItQoWnONzkWgzjFvctgnlhmq
I80AwowEEBYKAD4FgmSdh90ECwkHCAmQaBT7gxSTsXwDFQgKBBYAAgECGQEC
mwMCHgEWIQSvRnJTQT6TtdZFk0NoFPuDFJOxfAAAT7kBALmmUEJt5HMAOWiW
7/8y4wllm8zNQ9vbl5Q0cWbeWj/8AP9HDa2rRxHY/37g5zXdmL9f/qNWr9Fk
EBRhLLwusumuDMeLBGSdh90SCisGAQQBl1UBBQEBB0Am2yjjialeIVXHJJ2P
b7KiapCC0mD95F0EFz6zz0l4DgMBCAf+CRcISMdt0OUFCNUABB/OD0UW7MPK
Y3t8RrUTYoiCuhuPRDLOJ5NnMNagVQLt3jQsI8JRjzmYbiTrA/V3iJIEDu5C
NWbnvCM7Hs7+OqPzJPJ2w8J4BBgWCAAqBYJknYfdCZBoFPuDFJOxfAKbDBYh
BK9GclNBPpO11kWTQ2gU+4MUk7F8AADwfwD8CsOVw/3zm1UwUbGUi+fuf6Pr
VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu
0pYO
=rWL8
-----END PGP PRIVATE KEY BLOCK-----` });
      await expect(openpgp.decryptKey({
        privateKey: encryptedKeyUnknownS2K,
        passphrase: 'test'
      })).to.be.rejectedWith(/Key packet cannot be decrypted: unsupported S2K or cipher algo/);
    });
  });

  describe('encryptKey - unit tests', function() {
    it('should not change original key', async function() {
      const { privateKey: armoredKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }] });
      // read both keys from armored data to make sure all fields are exactly the same
      const key = await openpgp.readKey({ armoredKey });
      const originalKey = await openpgp.readKey({ armoredKey });
      return openpgp.encryptKey({
        privateKey: key,
        passphrase: passphrase
      }).then(locked => {
        expect(locked.getKeyID().toHex()).to.equal(key.getKeyID().toHex());
        expect(locked.subkeys[0].getKeyID().toHex()).to.equal(key.subkeys[0].getKeyID().toHex());
        expect(locked.isDecrypted()).to.be.false;
        expect(locked.keyPacket.privateParams).to.be.null;
        // original key should be unchanged
        expect(key.isDecrypted()).to.be.true;
        expect(key.keyPacket.privateParams).to.not.be.null;
        expect(key).to.deep.equal(originalKey);
      });
    });

    it('encrypted key can be decrypted', async function() {
      const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' });
      const locked = await openpgp.encryptKey({
        privateKey,
        passphrase: passphrase
      });
      expect(locked.isDecrypted()).to.be.false;
      const unlocked = await openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrase
      });
      expect(unlocked.isDecrypted()).to.be.true;
    });

    it('should throw on empty passphrase', async function() {
      const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' });
      await expect(openpgp.encryptKey({
        privateKey,
        passphrase: ''
      })).to.be.rejectedWith(/passphrase is required for key encryption/);
    });

    it('should support multiple passphrases', async function() {
      const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'test', email: 'test@test.com' }], format: 'object' });
      const passphrases = ['123', '456'];
      const locked = await openpgp.encryptKey({
        privateKey,
        passphrase: passphrases
      });
      expect(locked.isDecrypted()).to.be.false;
      await expect(openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrases[0]
      })).to.eventually.be.rejectedWith(/Incorrect key passphrase/);
      const unlocked = await openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrases
      });
      expect(unlocked.isDecrypted()).to.be.true;
    });

    it('should support encrypting with argon2 s2k', async function() {
      const key = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey });
      const locked = await openpgp.encryptKey({
        privateKey: key,
        passphrase: passphrase,
        config: {
          s2kType: openpgp.enums.s2k.argon2,
          aeadProtect: true
        }
      });
      expect(key.isDecrypted()).to.be.true;
      expect(locked.isDecrypted()).to.be.false;
      expect(locked.keyPacket.isDummy()).to.be.true;
      const unlocked = await openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrase
      });
      expect(key.isDecrypted()).to.be.true;
      expect(unlocked.isDecrypted()).to.be.true;
      expect(unlocked.keyPacket.isDummy()).to.be.true;
    });

    it('should encrypt gnu-dummy key', async function() {
      const key = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey });
      const locked = await openpgp.encryptKey({
        privateKey: key,
        passphrase: passphrase
      });
      expect(key.isDecrypted()).to.be.true;
      expect(locked.isDecrypted()).to.be.false;
      expect(locked.keyPacket.isDummy()).to.be.true;
      const unlocked = await openpgp.decryptKey({
        privateKey: locked,
        passphrase: passphrase
      });
      expect(key.isDecrypted()).to.be.true;
      expect(unlocked.isDecrypted()).to.be.true;
      expect(unlocked.keyPacket.isDummy()).to.be.true;
    });
  });

  describe('decrypt - unit tests', function() {
    let minRSABitsVal;

    beforeEach(() => {
      minRSABitsVal = openpgp.config.minRSABits;
      openpgp.config.minRSABits = 1024;
    });

    afterEach(function() {
      openpgp.config.minRSABits = minRSABitsVal;
    });

    it('Calling decrypt with encrypted key leads to exception', async function() {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const privateKey = await openpgp.readKey({ armoredKey: priv_key });

      const encOpt = {
        message: await openpgp.createMessage({ text: plaintext }),
        encryptionKeys: publicKey
      };
      const decOpt = {
        decryptionKeys: privateKey
      };
      const encrypted = await openpgp.encrypt(encOpt);
      decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
      await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith('Error decrypting message: Decryption key is not decrypted.');
    });

    ['binary', 'text'].forEach(format => {
      describe(`decrypt/verify with expectSigned=true, format=${format}`, function() {
        const message =
          format === 'binary' ? util.encodeUTF8(plaintext) :
            plaintext;

        it('decrypt/verify should succeed with valid signature (expectSigned=true)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            signingKeys: privateKey,
            encryptionKeys: publicKey
          });
          const { data, signatures } = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey,
            verificationKeys: publicKey,
            expectSigned: true,
            format
          });
          expect(data).to.deep.equal(message);
          expect(await signatures[0].verified).to.be.true;
        });

        it('decrypt/verify should throw on missing public keys (expectSigned=true)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          });
          await expect(openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey,
            expectSigned: true,
            format
          })).to.be.eventually.rejectedWith(/Verification keys are required/);
        });

        it('decrypt/verify should throw on missing signature (expectSigned=true)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            encryptionKeys: publicKey
          });
          await expect(openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey,
            verificationKeys: publicKey,
            expectSigned: true,
            format
          })).to.be.eventually.rejectedWith(/Message is not signed/);
        });

        it('decrypt/verify should throw on invalid signature (expectSigned=true)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          });
          await expect(openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey,
            verificationKeys: wrongPublicKey,
            expectSigned: true,
            format
          })).to.be.eventually.rejectedWith(/Could not find signing key/);
        });

        it('decrypt/verify should succeed with valid signature (expectSigned=true, with streaming)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            signingKeys: privateKey,
            encryptionKeys: publicKey
          });
          const { data: streamedData, signatures } = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
            decryptionKeys: privateKey,
            verificationKeys: publicKey,
            expectSigned: true,
            format
          });
          const data = await stream.readToEnd(streamedData);
          expect(data).to.deep.equal(message);
          expect(await signatures[0].verified).to.be.true;
        });

        it('decrypt/verify should throw on missing public keys (expectSigned=true, with streaming)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          });
          await expect(openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
            decryptionKeys: privateKey,
            expectSigned: true,
            format
          })).to.be.eventually.rejectedWith(/Verification keys are required/);
        });

        it('decrypt/verify should throw on missing signature (expectSigned=true, with streaming)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            encryptionKeys: publicKey
          });
          await expect(openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
            decryptionKeys: privateKey,
            verificationKeys: publicKey,
            expectSigned: true,
            format
          })).to.be.eventually.rejectedWith(/Message is not signed/);
        });

        it('decrypt/verify should throw on invalid signature (expectSigned=true, with streaming)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ [format]: message }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          });
          const { data: streamedData } = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
            decryptionKeys: privateKey,
            verificationKeys: wrongPublicKey,
            expectSigned: true,
            format
          });
          await expect(
            stream.readToEnd(streamedData)
          ).to.be.eventually.rejectedWith(/Could not find signing key/);
        });
      });
    });

    it('Supports decrypting with GnuPG dummy key', async function() {
      const { rejectMessageHashAlgorithms } = openpgp.config;
      Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
      try {
        const armoredMessage = `-----BEGIN PGP MESSAGE-----
Version: GnuPG v1.4.11 (GNU/Linux)

hQEOA1N4OCSSjECBEAP/diDJCQn4e88193PgqhbfAkohk9RQ0v0MPnXpJbCRTHKO
8r9nxiAr/TQv4ZOingXdAp2JZEoE9pXxZ3r1UWew04czxmgJ8FP1ztZYWVFAWFVi
Tj930TBD7L1fY/MD4fK6xjEG7z5GT8k4tn4mLm/PpWMbarIglfMopTy1M/py2cID
/2Sj7Ikh3UFiG+zm4sViYc5roNbMy8ixeoKixxi99Mx8INa2cxNfqbabjblFyc0Z
BwmbIc+ZiY2meRNI5y/tk0gRD7hT84IXGGl6/mH00bsX/kkWdKGeTvz8s5G8RDHa
Za4HgLbXItkX/QarvRS9kvkD01ujHfj+1ZvgmOBttNfP0p8BQLIICqvg1eYD9aPB
+GtOZ2F3+k5VyBL5yIn/s65SBjNO8Fqs3aL0x+p7s1cfUzx8J8a8nWpqq/qIQIqg
ZJH6MZRKuQwscwH6NWgsSVwcnVCAXnYOpbHxFQ+j7RbF/+uiuqU+DFH/Rd5pik8b
0Dqnp0yfefrkjQ0nuvubgB6Rv89mHpnvuJfFJRInpg4lrHwLvRwdpN2HDozFHcKK
aOU=
=4iGt
-----END PGP MESSAGE-----`;
        const passphrase = 'abcd';
        // exercises the GnuPG s2k type 1001 extension:
        // the secrets on the primary key have been stripped.
        const dummyKey = await openpgp.readKey({ armoredKey: armoredDummyPrivateKey1 });
        const publicKey = await openpgp.readKey({ armoredKey: armoredPublicKey1 });
        const message = await openpgp.readMessage({ armoredMessage });
        const primaryKeyPacket = dummyKey.keyPacket.write();
        expect(dummyKey.isDecrypted()).to.be.false;
        const decryptedDummyKey = await openpgp.decryptKey({ privateKey: dummyKey, passphrase });
        expect(decryptedDummyKey.isDecrypted()).to.be.true;
        // decrypting with a secret subkey works
        const msg = await openpgp.decrypt({
          message, decryptionKeys: decryptedDummyKey, verificationKeys: publicKey, config: { rejectPublicKeyAlgorithms: new Set() }
        });
        expect(msg.signatures).to.exist;
        expect(msg.signatures).to.have.length(1);
        expect(await msg.signatures[0].verified).to.be.true;
        expect((await msg.signatures[0].signature).packets.length).to.equal(1);
        // secret key operations involving the primary key should fail
        await expect(openpgp.sign({
          message: await openpgp.createMessage({ text: 'test' }), signingKeys: decryptedDummyKey, config: { rejectPublicKeyAlgorithms: new Set() }
        })).to.eventually.be.rejectedWith(/Cannot sign with a gnu-dummy key/);
        await expect(
          openpgp.reformatKey({ userIDs: { name: 'test' }, privateKey: decryptedDummyKey })
        ).to.eventually.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/);

        const encryptedDummyKey = await openpgp.encryptKey({ privateKey: decryptedDummyKey, passphrase });
        expect(encryptedDummyKey.isDecrypted()).to.be.false;
        const primaryKeyPacket2 = encryptedDummyKey.keyPacket.write();
        expect(primaryKeyPacket).to.deep.equal(primaryKeyPacket2);
      } finally {
        Object.assign(openpgp.config, { rejectMessageHashAlgorithms });
      }
    });

    it('supports decrypting a legacy AEAD message encrypted by OpenPGP.js v5 with `experimentalGCM` (AEADEncryptedDataPacket)', async () => {
      const plaintext = 'test';
      const passphrase = 'passphrase';
      const messageLegacyAEAD = await openpgp.readMessage({
        armoredMessage: `-----BEGIN PGP MESSAGE-----

w0oFCWQDCMbFipDX5vyLAFXhzn5i6iGJY/4BhPed85Yl62F1j8JWGT/8Mw3/
s7f058pohmXCztkTnrSo5+LUmRX8YwlGC5+5LbczD9Q8AQlkDHfOCyGb8NSF
mnk1YJIgLeTgPF4F1TK1ead1VfPqvUHK2Z/FzlaY94wK9f8QcA9RUSvjoKGH
BdPq
=+vdf
-----END PGP MESSAGE-----`,
        config: { enableParsingV5Entities: true }
      });

      const { data: decryptedData } = await openpgp.decrypt({
        message: messageLegacyAEAD,
        passwords: passphrase
      });

      expect(decryptedData).to.equal(plaintext);
    });

    it('decrypt with `config.constantTimePKCS1Decryption` option should succeed', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const publicKey2 = await openpgp.readKey({ armoredKey: eccPrivateKey });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKey,
        encryptionKeys: [publicKey, publicKey2]
      });
      const { data } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKey,
        config: { constantTimePKCS1Decryption: true }
      });
      expect(data).to.equal(plaintext);
    });

    it('decrypt with `config.constantTimePKCS1Decryption` option should succeed (with streaming)', async function () {
      const publicKey = await openpgp.readKey({ armoredKey: pub_key });
      const publicKey2 = await openpgp.readKey({ armoredKey: eccPrivateKey });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKey,
        encryptionKeys: [publicKey, publicKey2]
      });
      const { data: streamedData } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: stream.toStream(encrypted) }),
        decryptionKeys: privateKey,
        verificationKeys: publicKey,
        expectSigned: true,
        config: { constantTimePKCS1Decryption: true }
      });
      const data = await stream.readToEnd(streamedData);
      expect(data).to.equal(plaintext);
    });

    it('decrypt with `config.constantTimePKCS1Decryption` option should fail if session key algo support is disabled', async function () {
      const publicKeyRSA = await openpgp.readKey({ armoredKey: pub_key });
      const privateKeyRSA = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const privateKeyECC = await openpgp.readPrivateKey({ armoredKey: eccPrivateKey });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: plaintext }),
        signingKeys: privateKeyRSA,
        encryptionKeys: [publicKeyRSA, privateKeyECC]
      });

      const config = {
        constantTimePKCS1Decryption: true,
        constantTimePKCS1DecryptionSupportedSymmetricAlgorithms: new Set()
      };
      // decryption using RSA key should fail
      await expect(openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKeyRSA,
        config
      })).to.be.rejectedWith(/No decryption key packets found/);
      // decryption using ECC key should succeed (PKCS1 is not used, so constant time countermeasures are not applied)
      const { data } = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: privateKeyECC,
        config
      });
      expect(data).to.equal(plaintext);
    });

  });

  describe('verify - unit tests', function() {
    let minRSABitsVal;

    beforeEach(() => {
      minRSABitsVal = openpgp.config.minRSABits;
      openpgp.config.minRSABits = 512;
    });

    afterEach(function() {
      openpgp.config.minRSABits = minRSABitsVal;
    });

    ['binary', 'text', 'cleartext'].forEach(format => {
      describe(`verify ${format} message`, function() {
        const createMessage = format === 'cleartext' ? openpgp.createCleartextMessage : openpgp.createMessage;
        const readMessage = ({ armoredMessage }) => (
          format === 'cleartext' ?
            openpgp.readCleartextMessage({ cleartextMessage: armoredMessage }) :
            openpgp.readMessage({ armoredMessage })
        );
        const message =
          format === 'cleartext' ? util.removeTrailingSpaces(plaintext) :
            format === 'binary' ? util.encodeUTF8(plaintext) :
              plaintext;

        const property = format === 'cleartext' ? 'text' : format;

        it('verify should succeed with valid signature (expectSigned=true)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const signed = await openpgp.sign({
            message: await createMessage({ [property]: message }),
            signingKeys: privateKey
          });
          const { data, signatures } = await openpgp.verify({
            message: await readMessage({ armoredMessage: signed }),
            verificationKeys: publicKey,
            expectSigned: true,
            format
          });
          expect(data).to.deep.equal(message);
          expect(await signatures[0].verified).to.be.true;
        });

        it('verify should throw on missing signature (expectSigned=true)', async function () {
          const publicKey = await openpgp.readKey({ armoredKey: pub_key });

          await expect(openpgp.verify({
            message: await createMessage({ [property]: message }),
            verificationKeys: publicKey,
            expectSigned: true,
            format
          })).to.be.eventually.rejectedWith(/Message is not signed/);
        });

        it('verify should throw on invalid signature (expectSigned=true)', async function () {
          const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
          const privateKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase
          });

          const signed = await openpgp.sign({
            message: await createMessage({ [property]: message }),
            signingKeys: privateKey
          });
          await expect(openpgp.verify({
            message: await readMessage({ armoredMessage: signed }),
            verificationKeys: wrongPublicKey,
            expectSigned: true,
            format
          })).to.be.eventually.rejectedWith(/Could not find signing key/);
        });

        if (format !== 'cleartext') {
          it('verify should succeed with valid signature (expectSigned=true, with streaming)', async function () {
            const publicKey = await openpgp.readKey({ armoredKey: pub_key });
            const privateKey = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key }),
              passphrase
            });

            const signed = await openpgp.sign({
              message: await openpgp.createMessage({ [property]: stream.toStream(message) }),
              signingKeys: privateKey
            });
            const { data: streamedData, signatures } = await openpgp.verify({
              message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }),
              verificationKeys: publicKey,
              expectSigned: true,
              format
            });
            const data = await stream.readToEnd(streamedData);
            expect(data).to.deep.equal(message);
            expect(await signatures[0].verified).to.be.true;
          });

          it('verify should throw on missing signature (expectSigned=true, with streaming)', async function () {
            const publicKey = await openpgp.readKey({ armoredKey: pub_key });

            await expect(openpgp.verify({
              message: await openpgp.createMessage({ [property]: stream.toStream(message) }),
              verificationKeys: publicKey,
              expectSigned: true
            })).to.be.eventually.rejectedWith(/Message is not signed/);
          });

          it('verify should throw on invalid signature (expectSigned=true, with streaming)', async function () {
            const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
            const privateKey = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key }),
              passphrase
            });

            const signed = await openpgp.sign({
              message: await openpgp.createMessage({ [property]: stream.toStream(message) }),
              signingKeys: privateKey
            });
            const { data: streamedData } = await openpgp.verify({
              message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }),
              verificationKeys: wrongPublicKey,
              expectSigned: true
            });
            await expect(
              stream.readToEnd(streamedData)
            ).to.be.eventually.rejectedWith(/Could not find signing key/);
          });

          it('verify should fail if the signature is re-used with a different message', async function () {
            const privateKey = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key }),
              passphrase
            });

            const correctMessage = await createMessage({ [property]: message });
            const armoredSignature = await openpgp.sign({
              message: correctMessage,
              signingKeys: privateKey,
              detached: true
            });
            const { signatures } = await openpgp.verify({
              message: correctMessage,
              signature: await openpgp.readSignature({ armoredSignature }),
              verificationKeys: privateKey.toPublic()
            });
            expect(await signatures[0].verified).to.be.true;
            // pass a different message
            const wrongMessage =
              format === 'binary' ? util.encodeUTF8('a different message') :
                'a different message';
            await expect(openpgp.verify({
              message: await createMessage({ [property]: wrongMessage }),
              signature: await openpgp.readSignature({ armoredSignature }),
              verificationKeys: privateKey.toPublic(),
              expectSigned: true
            })).to.be.rejectedWith(/digest did not match/);
          });
        }
      });
    });
  });

  describe('sign - unit tests', function() {
    it('Supports signing with GnuPG dummy key', async function() {
      const dummyKey = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey });
      const sig = await openpgp.sign({
        message: await openpgp.createMessage({ text: 'test' }),
        signingKeys: dummyKey,
        date: new Date('2018-12-17T03:24:00'),
        config: { minRSABits: 1024 }
      });
      expect(sig).to.match(/-----END PGP MESSAGE-----\n$/);
    });

    it('Calling sign with no signing key leads to exception', async function() {
      await expect(openpgp.sign({
        message: await openpgp.createMessage({ text: plaintext })
      })).to.be.rejectedWith(/No signing keys provided/);
    });

    it('Signing with key which uses sha3 should generate a valid sha3 signature if `config.preferredHashAlgorithm` has been set accordingly', async function() {
      const privKey = await openpgp.readKey({ armoredKey: priv_key_sha3_512 });
      const pubKey = privKey.toPublic();
      const text = 'Hello, world.';
      const message = await openpgp.createCleartextMessage({ text });

      const cleartextMessage = await openpgp.sign({ message, signingKeys: privKey, format: 'armored' });
      const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
      expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
      expect(
        parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
      ).to.equal(openpgp.config.preferredHashAlgorithm);
      const cleartextMessageWithSHA3 = await openpgp.sign({ message, signingKeys: privKey, format: 'armored', config: { preferredHashAlgorithm: openpgp.enums.hash.sha3_512 } });
      const parsedArmoredSHA3 = await openpgp.readCleartextMessage({ cleartextMessage: cleartextMessageWithSHA3 });
      expect(parsedArmoredSHA3.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
      expect(
        parsedArmoredSHA3.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
      ).to.equal(openpgp.enums.hash.sha3_512);

      const verified = await openpgp.verify({ message: parsedArmored, verificationKeys: pubKey, expectSigned: true });
      const verifiedSHA3 = await openpgp.verify({ message: parsedArmoredSHA3, verificationKeys: pubKey, expectSigned: true });
      expect(verified.data).to.equal(text);
      expect(verifiedSHA3.data).to.equal(text);
    });

    it('should output cleartext message of expected format', async function() {
      const text = 'test';
      const message = await openpgp.createCleartextMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const cleartextMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' });
      const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
      expect(parsedArmored.text).to.equal(text);
      expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      await expect(openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' })).to.be.rejectedWith('');

      const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
      const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format', async function() {
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
      const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format', async function() {
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
      const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format (with streaming)', async function() {
      const text = 'test';
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredMessage = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        format: 'armored',
        config
      });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const binaryMessage = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        format: 'binary',
        config
      });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);

      const objectMessage = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        format: 'object',
        config
      });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
      objectMessage.packets[1].data = await stream.readToEnd(objectMessage.packets[1].data);
      objectMessage.packets[2].signedHashValue = await stream.readToEnd(objectMessage.packets[2].signedHashValue);
      const { data: streamedData } = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
      expect(await stream.readToEnd(streamedData)).to.equal(text);
      expect(streamedData).to.equal(text);
    });

    it('should output message of expected format (detached)', async function() {
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'armored' });
      const parsedArmored = await openpgp.readSignature({ armoredSignature });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const binarySignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'binary' });
      const parsedBinary = await openpgp.readSignature({ binarySignature });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const objectSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'object' });
      expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
      const verified = await openpgp.verify({ message, signature: objectSignature, verificationKeys: privateKey, expectSigned: true, config });
      expect(verified.data).to.equal(text);
    });

    it('should output message of expected format (detached, with streaming)', async function() {
      const text = 'test';
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });
      const config = { minRSABits: 1024 };

      const armoredSignature = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        detached: true,
        format: 'armored',
        config
      });
      const parsedArmored = await openpgp.readSignature({ armoredSignature: await stream.readToEnd(armoredSignature) });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const binarySignature = await openpgp.sign({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        signingKeys: privateKey,
        detached: true,
        format: 'binary',
        config
      });
      const parsedBinary = await openpgp.readSignature({ binarySignature: await stream.readToEnd(binarySignature) });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const streamedMessage = await openpgp.createMessage({ text: stream.toStream(text) });
      const objectSignature = await openpgp.sign({
        message: streamedMessage,
        signingKeys: privateKey,
        detached: true,
        format: 'object',
        config
      });
      expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);

      const armoredStreamedMessage = streamedMessage.armor(); // consume input message stream, to allow to read the signed hash
      objectSignature.packets[0].signedHashValue = await stream.readToEnd(objectSignature.packets[0].signedHashValue);
      const { data: streamedData } = await openpgp.verify({
        message: await openpgp.readMessage({ armoredMessage: armoredStreamedMessage }),
        signature: objectSignature,
        verificationKeys: privateKey,
        expectSigned: true,
        config
      });
      expect(await stream.readToEnd(streamedData)).to.equal(text);
    });

    it('should sign using hash algorithm preferred by `recipientKeys` if given', async function() {
      const signingKeyWithoutSHA3Pref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEZyID/RYJKwYBBAHaRw8BAQdAcdaUl/UXEQaT6rKNSEPmyKypikz9rIsf
BlFAQYjtsF8AAQDiW9ls2uBBRa3vA1Odl0NNNguRBolWhR9XGpdXnVBF3w5E
zQ48dGVzdEB0ZXN0Lml0PsLAEQQTFgoAgwWCZyID/QMLCQcJkJuH6wXn78D5
RRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmcNolfauRaj
NnItFJ0TOsiyZZhd6bMWVR4032v64tYRywMVCAoEFgACAQIZAQKbAwIeARYh
BGsOUiBRfu57iwuxh5uH6wXn78D5AACEQQEAz4YXoEKgOElvxRrIrkglUlpb
ilLZVU6mXqLxRSEtZi0BAK5xooNiLYbjF42eJuCDWUWriXufI9acT/vnruFr
p34Px10EZyID/RIKKwYBBAGXVQEFAQEHQOC8KcmOQ9+qEgoWBzc8xNgPUvoe
IVNw+mHbljD9eFBfAwEIBwAA/3iHMqnBfuM/c9tOIWKI4advW92aMYnjexrU
HdzPS2IoEU3CvgQYFgoAcAWCZyID/QmQm4frBefvwPlFFAAAAAAAHAAgc2Fs
dEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ5M4VuJhTqDkHF/14D0i/wL8GTtM
fm9AIukMoYWXjGSGApsMFiEEaw5SIFF+7nuLC7GHm4frBefvwPkAAL0QAP9Z
oR7Vxyfuje3vAyEbef1gyfMN/RkIVbMKSiwy3A2W9AEA6QcBF5zUvwmHPpA4
+SkLLMuq/yUGT6WhAq6kASQ8vgM=
=lluz
-----END PGP PRIVATE KEY BLOCK-----` });
      const recipientKeyWithSHA3Pref = await openpgp.readKey({ armoredKey: priv_key_sha3_512 });

      const text = 'Hello, world.';
      const message = await openpgp.createCleartextMessage({ text });

      // SHA3-512 is first preference of recipient key, and should be picked,
      // even if not declared in the signing key prefs
      const cleartextMessage = await openpgp.sign({
        message,
        signingKeys: signingKeyWithoutSHA3Pref,
        recipientKeys: recipientKeyWithSHA3Pref,
        format: 'armored',
        // the preferred hash algo is expected to picked when supported by the recipient keys
        config: { preferredHashAlgorithm: openpgp.enums.hash.sha3_512 }
      });
      const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
      expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
      expect(
        parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
      ).to.equal(openpgp.enums.hash.sha3_512);
    });
  });

  describe('encrypt - unit tests', function() {
    it('should output message of expected format', async function() {
      const passwords = 'password';
      const text = 'test';
      const message = await openpgp.createMessage({ text });
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const armoredMessage = await openpgp.encrypt({ message, passwords, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const binaryMessage = await openpgp.encrypt({ message, passwords, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const config = { minRSABits: 1024 };
      const objectMessage = await openpgp.encrypt({ message, passwords, signingKeys: privateKey, config, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
      const decrypted = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config });
      expect(decrypted.data).to.equal(text);
    });

    it('should output message of expected format (with streaming)', async function() {
      const passwords = 'password';
      const text = 'test';
      const privateKey = await openpgp.decryptKey({
        privateKey: await openpgp.readKey({ armoredKey: priv_key }),
        passphrase
      });

      const armoredMessage = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        passwords,
        format: 'armored'
      });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const binaryMessage = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        passwords,
        format: 'binary'
      });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const config = { minRSABits: 1024 };
      const objectMessage = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: stream.toStream(text) }),
        passwords,
        signingKeys: privateKey,
        format: 'object',
        config
      });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
      const { data: streamedData } = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config });
      expect(await stream.readToEnd(streamedData)).to.equal(text);
    });

    it('should support encrypting with encrypted key with unknown s2k (unparseableKeyMaterial)', async function() {
      const originalDecryptedKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEZJ2H3RYJKwYBBAHaRw8BAQdA3V39Xv0+436Rpn/2UlcnOC1BGprmAlWY
RBKjAq0hAtAAAQCykslk/kEP7+O9sOsuvgX2Xfz4peQuNo2vD/w4dMZpchKj
zQDCjAQQFgoAPgWCZJ2H3QQLCQcICZBoFPuDFJOxfAMVCAoEFgACAQIZAQKb
AwIeARYhBK9GclNBPpO11kWTQ2gU+4MUk7F8AABPuQEAuaZQQm3kcwA5aJbv
/zLjCWWbzM1D29uXlDRxZt5aP/wA/0cNratHEdj/fuDnNd2Yv1/+o1av0WQQ
FGEsvC6y6a4Mx10EZJ2H3RIKKwYBBAGXVQEFAQEHQCbbKOOJqV4hVccknY9v
sqJqkILSYP3kXQQXPrPPSXgOAwEIBwAA/34s+u8hyLdzdLxjrEEN9zNb+C8d
EyBNxMpyZ/NJsUxoEIPCeAQYFggAKgWCZJ2H3QmQaBT7gxSTsXwCmwwWIQSv
RnJTQT6TtdZFk0NoFPuDFJOxfAAA8H8A/ArDlcP985tVMFGxlIvn7n+j61RQ
SxvLnPSImimp5/w2AQCXGTWR0Un6Q6FdqVAORABItjHY8ZEcrBUJ2D0QLtKW
Dg==
=wiwz
-----END PGP PRIVATE KEY BLOCK-----` });
      // `originalDecryptedKey` encrypted with invalid s2kType = 23, to test that it can still be used for encryption/verification
      const encryptedKeyUnknownS2K = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xYYEZJ2H3RYJKwYBBAHaRw8BAQdA3V39Xv0+436Rpn/2UlcnOC1BGprmAlWY
RBKjAq0hAtD+CRcIdHzwqoLa54cAbBOEIgBh7Xa1Qh5wCGAmEVWnAldaqvk+
NcvUL2bR6AQsGIT6YEihOS3xLKobMOd2XlO5ItQoWnONzkWgzjFvctgnlhmq
I80AwowEEBYKAD4FgmSdh90ECwkHCAmQaBT7gxSTsXwDFQgKBBYAAgECGQEC
mwMCHgEWIQSvRnJTQT6TtdZFk0NoFPuDFJOxfAAAT7kBALmmUEJt5HMAOWiW
7/8y4wllm8zNQ9vbl5Q0cWbeWj/8AP9HDa2rRxHY/37g5zXdmL9f/qNWr9Fk
EBRhLLwusumuDMeLBGSdh90SCisGAQQBl1UBBQEBB0Am2yjjialeIVXHJJ2P
b7KiapCC0mD95F0EFz6zz0l4DgMBCAf+CRcISMdt0OUFCNUABB/OD0UW7MPK
Y3t8RrUTYoiCuhuPRDLOJ5NnMNagVQLt3jQsI8JRjzmYbiTrA/V3iJIEDu5C
NWbnvCM7Hs7+OqPzJPJ2w8J4BBgWCAAqBYJknYfdCZBoFPuDFJOxfAKbDBYh
BK9GclNBPpO11kWTQ2gU+4MUk7F8AADwfwD8CsOVw/3zm1UwUbGUi+fuf6Pr
VFBLG8uc9IiaKann/DYBAJcZNZHRSfpDoV2pUA5EAEi2MdjxkRysFQnYPRAu
0pYO
=rWL8
-----END PGP PRIVATE KEY BLOCK-----` });
      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: 'test' }),
        encryptionKeys: encryptedKeyUnknownS2K
      });

      // decrypt with original key
      const decrypted = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: originalDecryptedKey
      });
      expect(decrypted.data).to.equal('test');
    });

    it('does not encrypt to expired key (expiration time subpacket on a direct-key signature)', async function() {
      const expiredKey = await openpgp.readKey({ armoredKey: expiredPublicKeyThroughDirectSignature });
      await expect(
        openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: expiredKey })
      ).to.be.rejectedWith(/Primary key is expired/);
    });

    it('uses AEAD when the encryption key prefs support it (SEIPDv2', async function() {
      const v4PrivateKeyWithOCBPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });
      const v6PrivateKeyWithOCBPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });

      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: 'test' }),
        encryptionKeys: [v4PrivateKeyWithOCBPref, v6PrivateKeyWithOCBPref],
        format: 'object'
      });

      const seipd = encrypted.packets[2];
      expect(seipd).to.be.instanceOf(openpgp.SymEncryptedIntegrityProtectedDataPacket);
      expect(seipd.version).to.equal(2);
      expect(seipd.aeadAlgorithm).to.equal(openpgp.enums.aead.ocb);
    });

    it('should support encrypting to a key without features (missing SEIPDv1 feature)', async function () {
      const key = await openpgp.readKey({ armoredKey: rsaKeyWithNoFeaturesFromRNP });
      const encrypted = await openpgp.encrypt({
        message: await openpgp.createMessage({ text: 'test' }),
        encryptionKeys: key
      });
      const decrypted = await openpgp.decrypt({
        message: await openpgp.readMessage({ armoredMessage: encrypted }),
        decryptionKeys: key
      });
      expect(decrypted.data).to.equal('test');
    });
  });

  describe('encryptSessionKey - unit tests', function() {
    it('should output message of expected format', async function() {
      const passwords = 'password';
      const sessionKey = { data: new Uint8Array(16).fill(1), algorithm: 'aes128' };

      const armoredMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'armored' });
      const parsedArmored = await openpgp.readMessage({ armoredMessage });
      expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const binaryMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'binary' });
      const parsedBinary = await openpgp.readMessage({ binaryMessage });
      expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);

      const objectMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'object' });
      expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
      const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ message: objectMessage, passwords });
      expect(decryptedSessionKey).to.deep.equal(sessionKey);
    });

    it('passing no encryption keys or passwords leads to exception', async function() {
      await expect(openpgp.encryptSessionKey({
        algorithm: 'aes256',
        data: util.hexToUint8Array('3e99c1bb485e70a1fcef09a7ad8d38d171015243bbdd853e1a2b0e334d122ff3')
      })).to.be.rejectedWith(/No encryption keys or passwords provided/);
    });

    // keep this after the 'memory-heavy' test to confirm that the Wasm module was successfully reloaded
    it('supports encrypting with argon2 s2k', async function() {
      const config = { s2kType: openpgp.enums.s2k.argon2 };
      const passwords = 'password';
      const sessionKey = {
        algorithm: 'aes128',
        data: util.hexToUint8Array('01FE16BBACFD1E7B78EF3B865187374F')
      };
      const encrypted = await openpgp.encryptSessionKey({ ...sessionKey, passwords, config, format: 'object' });
      expect(encrypted.packets).to.have.length(1);
      const skesk = encrypted.packets[0];
      expect(skesk.s2k.type).to.equal('argon2');
      const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ message: encrypted, passwords });
      expect(decryptedSessionKey).to.deep.equal(sessionKey);
    });
  });

  describe('decryptSessionKeys - unit tests', function() {
    it('should decrypt message with two SKESKs where the wrong password returns a symmetric algo equal to 0', async function () {
      // SKESK packets do not have an intrisic integrity check, and the session key must be used to check its validity.
      // This message is such that when the password is used to try and decrypt the first (mismatching) SKESK, the result returns a '0' byte for
      // the session key algo. This corresponds to the 'plaintext' algo identifier (https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-12.2.1),
      // now removed from OpenPGP.js .
      // This test guards against regressions caused by mishandling this value on SKESK decryption.
      const message = await openpgp.readMessage({ armoredMessage:`-----BEGIN PGP MESSAGE-----

wy4ECQMIf6HA/a6XkOAAlaU1z+uVfU5kmHmqNqahQH859nAMresQ6w1uIjsL
ZE5bwy4ECQMIKVfCf+GWBesA5KFPuIDa6kLLn/AvEgbCi5DOg2xdIf73SNSN
Tqy7nfex0sANAVtHHRkHVTRVTVa3MFjjiWeBEDtnfyVMntWJ21ihrIU9eb9p
qS7UljZZ0u++xSWclU2IGBXCIdO0wLuS6hYk3q5OFexWj8OIoYJX88nkA2iW
5xyGd9EFRWVsR4CREt8lwrIE2t/h8XpRlhJmY6Iefg8+2DeN8vDdhNs/B02o
0zAE0hF+3xvwZTLi4hDrhBZEgBedPaeX4jlmDc3qzh2wlgV/Mq/FUakYfSLl
bc1PCpfqkLZSuCfv4eoTrWohWi9lS/pRXY9hzzHtlnjo6w==
-----END PGP MESSAGE-----` });
      const sessionKeys = await openpgp.decryptSessionKeys({ message, passwords: 'I am another password' });
      expect(sessionKeys).to.have.length(1); // first SKESK dropped due to '0' being treated as invalid algo identifier
      expect(sessionKeys[0].algorithm).to.equal('aes256');

      // decrypt() used to fail on this type of input
      const decrypted = await openpgp.decrypt({ message, passwords: 'I am another password', format: 'binary' });
      expect(decrypted.data).to.deep.equal(
        util.hexToUint8Array('e280872009d699e0b5bae18ba1e28c86d280d184e1b888e1a8b3e28ab7e0afa8e0b98be283bde28db5e1b1afd48de1b5b2e280a7e199b0e1a487e185afd3a1e18ca5e0bfb4e28892e19e98e29bb8e29594e0baaae0b681e1929af09f9882f09f9897f09f9882f09f98abf09f988ff09f98b6f09f98bcf09f98bcf09f9981f09f9982e2808720090d0aed959ceab5adec96b42feca1b0ec84a0eba790')
      );
      expect(decrypted.signatures.length).to.equal(0);
    });

    it('should decrypt PKESK v6 and return a null symmetric algorithm', async function() {
      // test vector https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.8
      const armoredMessage = `-----BEGIN PGP MESSAGE-----

wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
bhF30A+IitsxxA==
-----END PGP MESSAGE-----`;

      const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });

      const sessionKeys = await openpgp.decryptSessionKeys({
        message: await openpgp.readMessage({ armoredMessage }),
        decryptionKeys: privateKey
      });

      expect(sessionKeys).to.have.length(1);
      expect(sessionKeys[0].algorithm).to.equal(null); // PKESK v6 does not include the algo info
    });

    it('supports decrypting with argon2 s2k (memory-heavy params)', async function() {
      const passwords = 'password';
      // Test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#appendix-A.8.1
      const armoredMessage = `-----BEGIN PGP MESSAGE-----
Comment: Encrypted using AES with 128-bit key
Comment: Session key: 01FE16BBACFD1E7B78EF3B865187374F

wycEBwScUvg8J/leUNU1RA7N/zE2AQQVnlL8rSLPP5VlQsunlO+ECxHSPgGYGKY+
YJz4u6F+DDlDBOr5NRQXt/KJIf4m4mOlKyC/uqLbpnLJZMnTq3o79GxBTdIdOzhH
XfA3pqV4mTzF
-----END PGP MESSAGE-----`;
      const expectedSessionKey = util.hexToUint8Array('01FE16BBACFD1E7B78EF3B865187374F');

      try {
        const [decryptedSessionKey] = await openpgp.decryptSessionKeys({
          message: await openpgp.readMessage({ armoredMessage }),
          passwords
        });
        expect(decryptedSessionKey.data).to.deep.equal(expectedSessionKey);
        expect(decryptedSessionKey.algorithm).to.equal('aes128');
      } catch (err) {
        if (detectBrowser()) { // Expected to fail in the CI, especially in Browserstack
          expect(err.message).to.match(/Could not allocate required memory/);
        }
      }
    });
  });

  describe('encrypt, decrypt, sign, verify - integration tests', function() {
    let privateKey_2000_2008;
    let publicKey_2000_2008;
    let privateKey_2038_2045;
    let publicKey_2038_2045;
    let privateKey_1337;
    let publicKey_1337;
    let privateKey;
    let publicKey;
    let publicKeyNoAEAD;

    let aeadProtectVal;
    let preferredAEADAlgorithmVal;
    let aeadChunkSizeByteVal;
    let v6KeysVal;
    let minRSABitsVal;

    beforeEach(async function() {
      publicKey = await openpgp.readKey({ armoredKey: pub_key });
      publicKeyNoAEAD = await openpgp.readKey({ armoredKey: pub_key });
      privateKey = await openpgp.readKey({ armoredKey: priv_key });
      privateKey_2000_2008 = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
      publicKey_2000_2008 = privateKey_2000_2008.toPublic();
      privateKey_2038_2045 = await openpgp.readKey({ armoredKey: priv_key_2038_2045 });
      publicKey_2038_2045 = privateKey_2038_2045.toPublic();
      privateKey_1337 = await openpgp.readKey({ armoredKey: priv_key_expires_1337 });
      publicKey_1337 = privateKey_1337.toPublic();

      aeadProtectVal = openpgp.config.aeadProtect;
      preferredAEADAlgorithmVal = openpgp.config.preferredAEADAlgorithm;
      aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
      v6KeysVal = openpgp.config.v6Keys;
      minRSABitsVal = openpgp.config.minRSABits;

      openpgp.config.minRSABits = 512;
    });

    afterEach(function() {
      openpgp.config.aeadProtect = aeadProtectVal;
      openpgp.config.preferredAEADAlgorithm = preferredAEADAlgorithmVal;
      openpgp.config.aeadChunkSizeByte = aeadChunkSizeByteVal;
      openpgp.config.v6Keys = v6KeysVal;
      openpgp.config.minRSABits = minRSABitsVal;
    });

    it('Configuration', async function() {
      const showCommentVal = openpgp.config.showComment;
      const showVersionVal = openpgp.config.showVersion;
      const commentStringVal = openpgp.config.commentString;

      try {
        const encryptedDefault = await openpgp.encrypt({ encryptionKeys:publicKey, message:await openpgp.createMessage({ text: plaintext }) });
        expect(encryptedDefault).to.exist;
        expect(encryptedDefault).not.to.match(/^Version:/);
        expect(encryptedDefault).not.to.match(/^Comment:/);

        openpgp.config.showComment = true;
        openpgp.config.commentString = 'different';
        const encryptedWithComment = await openpgp.encrypt({ encryptionKeys:publicKey, message:await openpgp.createMessage({ text: plaintext }) });
        expect(encryptedWithComment).to.exist;
        expect(encryptedWithComment).not.to.match(/^Version:/);
        expect(encryptedWithComment).to.match(/Comment: different/);
      } finally {
        openpgp.config.showComment = showCommentVal;
        openpgp.config.showVersion = showVersionVal;
        openpgp.config.commentString = commentStringVal;
      }
    });

    tryTests('CFB mode', tests, {
      if: true,
      beforeEach: function() {
        openpgp.config.aeadProtect = false;
      }
    });

    tryTests('GCM mode (V6 keys)', tests, {
      if: true,
      beforeEach: function() {
        openpgp.config.aeadProtect = true;
        openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.gcm;
        openpgp.config.v6Keys = true;

        // Monkey-patch SEIPD V2 feature flag
        publicKey.users[0].selfCertifications[0].features = [9];
        publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
        publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
      }
    });

    tryTests('EAX mode (small chunk size)', tests, {
      if: true,
      beforeEach: function() {
        openpgp.config.aeadProtect = true;
        openpgp.config.aeadChunkSizeByte = 0;
        openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax;

        // Monkey-patch SEIPD V2 feature flag
        publicKey.users[0].selfCertifications[0].features = [9];
        publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
        publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
      }
    });

    tryTests('OCB mode', tests, {
      if: true,
      beforeEach: function() {
        openpgp.config.aeadProtect = true;
        openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb;

        // Monkey-patch SEIPD V2 feature flag
        publicKey.users[0].selfCertifications[0].features = [9];
        publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
        publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
      }
    });

    function tests() {
      describe('encryptSessionKey, decryptSessionKeys', function() {
        const sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]);

        let decryptedPrivateKey; // to avoid decrypting key before each test
        beforeEach(async function() {
          if (!decryptedPrivateKey) {
            decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase });
          }
          privateKey = decryptedPrivateKey;
        });

        it('should encrypt with public key', function() {
          return openpgp.encryptSessionKey({
            data: sk,
            algorithm: 'aes128',
            encryptionKeys: publicKey,
            format: 'binary'
          }).then(async function(encrypted) {
            const message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decryptSessionKeys({
              message,
              decryptionKeys: privateKey
            });
          }).then(function(decrypted) {
            expect(decrypted[0].data).to.deep.equal(sk);
          });
        });

        it('should encrypt with password', function() {
          return openpgp.encryptSessionKey({
            data: sk,
            algorithm: 'aes128',
            passwords: password1,
            format: 'binary'
          }).then(async function(encrypted) {
            const message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decryptSessionKeys({
              message,
              passwords: password1
            });
          }).then(function(decrypted) {
            expect(decrypted[0].data).to.deep.equal(sk);
          });
        });

        it('should not decrypt with a key without binding signatures', function() {
          return openpgp.encryptSessionKey({
            data: sk,
            algorithm: 'aes128',
            encryptionKeys: publicKey,
            format: 'binary'
          }).then(async function(encrypted) {
            const message = await openpgp.readMessage({ binaryMessage: encrypted });
            const invalidPrivateKey = await openpgp.readKey({ armoredKey: priv_key });
            invalidPrivateKey.subkeys[0].bindingSignatures = [];
            return openpgp.decryptSessionKeys({
              message,
              decryptionKeys: invalidPrivateKey
            }).then(() => {
              throw new Error('Should not decrypt with invalid key');
            }).catch(error => {
              expect(error.message).to.match(/Error decrypting session keys: Could not find valid subkey binding signature in key/);
            });
          });
        });

        it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with pgp key pair', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with pgp key pair -- trailing spaces', async function () {
          const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            decryptionKeys: privateKey
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt, decryptSessionKeys, decrypt with password', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: password1
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            passwords: password1
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt with multiple passwords, decryptSessionKeys, decrypt with multiple passwords', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: [password1, password2]
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            passwords: [password1, password2]
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });

        it('roundtrip workflow: encrypt twice with one password, decryptSessionKeys, only one session key', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: [password1, password1]
          });
          const decryptedSessionKeys = await openpgp.decryptSessionKeys({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            passwords: password1
          });
          expect(decryptedSessionKeys.length).to.equal(1);
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: decryptedSessionKeys[0]
          });
          expect(decrypted.data).to.equal(plaintext);
        });
      });

      describe('AES / RSA encrypt, decrypt, sign, verify', function() {
        const wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' +
          'Version: OpenPGP.js v0.9.0\r\n' +
          'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' +
          '\r\n' +
          'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' +
          'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' +
          'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' +
          'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' +
          'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
          '=6XMW\r\n' +
          '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n';

        let decryptedPrivateKey; // to avoid decrypting key before each test
        beforeEach(async function() {
          if (!decryptedPrivateKey) {
            decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase });
          }
          privateKey = decryptedPrivateKey;
        });

        it('should encrypt then decrypt', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          };
          const decOpt = {
            decryptionKeys: privateKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures).to.exist;
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt then decrypt with multiple private keys', async function () {
          const privKeyDE = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
            passphrase
          });

          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          };
          const decOpt = {
            decryptionKeys: [privKeyDE, privateKey]
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures).to.exist;
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt then decrypt with wildcard (anonymous recipient)', async function () {
          const { privateKey: privateKeyV4orV6 } = await openpgp.generateKey({ userIDs: { email: 'test@test.it' }, format: 'object' });
          const plaintext = 'hello world';

          const encryptedMessage = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: privateKeyV4orV6,
            wildcard: true,
            format: 'object'
          });

          expect(encryptedMessage.getEncryptionKeyIDs().every(keyID => keyID.isWildcard())).to.be.true;
          const armoredMessage = encryptedMessage.armor();

          const parsedEncryptedMessage = await openpgp.readMessage({ armoredMessage });
          expect(parsedEncryptedMessage.getEncryptionKeyIDs().every(keyID => keyID.isWildcard())).to.be.true;

          const decrypted = await openpgp.decrypt({ message: parsedEncryptedMessage, decryptionKeys: privateKeyV4orV6 });
          expect(decrypted.data).to.equal(plaintext);
          expect(decrypted.signatures).to.exist;
          expect(decrypted.signatures.length).to.equal(0);
        });

        it('should encrypt then decrypt with wildcard with multiple private keys (anonymous recipient)', async function () {
          const privKeyDE = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
            passphrase
          });

          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            wildcard: true
          };
          const decOpt = {
            decryptionKeys: [privKeyDE, privateKey]
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures).to.exist;
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt then decrypt using returned session key', async function () {
          const sessionKey = await openpgp.generateSessionKey({
            encryptionKeys: publicKey
          });
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            sessionKey
          });
          expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            sessionKeys: sessionKey
          });
          expect(decrypted.data).to.equal(plaintext);
          expect(decrypted.signatures).to.exist;
          expect(decrypted.signatures.length).to.equal(0);
        });

        it('should encrypt using custom session key and decrypt using session key', async function () {
          const sessionKey = {
            data: crypto.generateSessionKey(openpgp.enums.symmetric.aes256),
            algorithm: 'aes256'
          };
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            sessionKey: sessionKey,
            encryptionKeys: publicKey
          };
          const decOpt = {
            sessionKeys: sessionKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(false);
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
          });
        });

        it('should encrypt using custom session key and decrypt using private key', async function () {
          const sessionKey = {
            data: crypto.generateSessionKey(openpgp.enums.symmetric.aes128),
            algorithm: 'aes128'
          };
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            sessionKey: sessionKey,
            encryptionKeys: publicKey
          };
          const decOpt = {
            decryptionKeys: privateKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(false);
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
          });
        });

        it('should encrypt/sign and decrypt/verify', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            const supportsSEIPDv2 = !!(publicKey.users[0].selfCertifications[0].features?.[0] & openpgp.enums.features.seipdv2);
            expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify (expectSigned=true)', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKey,
            expectSigned: true
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            const supportsSEIPDv2 = !!(publicKey.users[0].selfCertifications[0].features?.[0] & openpgp.enums.features.seipdv2);
            expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify (no AEAD support)', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKeyNoAEAD,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKeyNoAEAD
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(1);
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify with generated key', function () {
          const genOpt = {
            userIDs: [{ name: 'Test User', email: 'text@example.com' }]
          };

          return openpgp.generateKey(genOpt).then(async function(newKey) {
            const supportsSEIPDv2 = openpgp.config.aeadProtect;
            const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey });
            const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey });

            const encOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              encryptionKeys: newPublicKey,
              signingKeys: newPrivateKey
            };
            const decOpt = {
              decryptionKeys: newPrivateKey,
              verificationKeys: newPublicKey
            };
            return openpgp.encrypt(encOpt).then(async function (encrypted) {
              decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
              expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
              return openpgp.decrypt(decOpt);
            }).then(async function (decrypted) {
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              const signingKey = await newPrivateKey.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
            });
          });
        });

        it('should encrypt/sign and decrypt/verify with generated key and detached signatures', async function () {
          const newKey = await openpgp.generateKey({
            userIDs: [{ name: 'Test User', email: 'text@example.com' }]
          });
          const supportsSEIPDv2 = openpgp.config.aeadProtect;
          const newPublicKey = await openpgp.readKey({ armoredKey: newKey.publicKey });
          const newPrivateKey = await openpgp.readKey({ armoredKey: newKey.privateKey });

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: newPublicKey
          });
          const signed = await openpgp.sign({
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: newPrivateKey,
            detached: true
          });
          const message = await openpgp.readMessage({ armoredMessage: encrypted });
          expect(message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version).to.equal(supportsSEIPDv2 ? 2 : 1);
          const decrypted = await openpgp.decrypt({
            message,
            signature: await openpgp.readSignature({ armoredSignature: signed }),
            decryptionKeys: newPrivateKey,
            verificationKeys: newPublicKey
          });
          expect(decrypted.data).to.equal(plaintext);
          expect(await decrypted.signatures[0].verified).to.be.true;
          const signingKey = await newPrivateKey.getSigningKey();
          expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
        });

        it('should encrypt/sign and decrypt/verify with null string input', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: '' }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: publicKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function (decrypted) {
            expect(decrypted.data).to.equal('');
            expect(await decrypted.signatures[0].verified).to.be.true;
            const signingKey = await privateKey.getSigningKey();
            expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should encrypt/sign and decrypt/verify with detached signatures', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const signed = await openpgp.sign({
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: privateKey,
            detached: true
          });
          const decrypted = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            signature: await openpgp.readSignature({ armoredSignature: signed }),
            decryptionKeys: privateKey,
            verificationKeys: publicKey
          });
          expect(decrypted.data).to.equal(plaintext);
          expect(await decrypted.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
        });

        it('should encrypt and decrypt/verify with detached signature as input for encryption', async function () {
          const { rejectPublicKeyAlgorithms } = openpgp.config;
          try {
            openpgp.config.rejectPublicKeyAlgorithms = new Set();

            const plaintext = '  \t┍ͤ޵၂༫዇◧˘˻ᙑ᎚⏴ំந⛑nٓኵΉⅶ⋋ŵ⋲΂ͽᣏ₅ᄶɼ┋⌔û᬴Ƚᔡᧅ≃ṱἆ⃷݂૿ӌ᰹෇ٹჵ⛇໶⛌  \t\n한국어/조선말';

            const privKeyDE = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
              passphrase
            });

            const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });

            const signOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              signingKeys: privKeyDE,
              detached: true
            };

            const encOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              encryptionKeys: publicKey,
              signingKeys: privateKey
            };

            const decOpt = {
              decryptionKeys: privateKey,
              verificationKeys: [publicKey, pubKeyDE]
            };

            await openpgp.sign(signOpt).then(async function (armoredSignature) {
              encOpt.signature = await openpgp.readSignature({ armoredSignature });
              return openpgp.encrypt(encOpt);
            }).then(async function (armoredMessage) {
              decOpt.message = await openpgp.readMessage({ armoredMessage });
              return openpgp.decrypt(decOpt);
            }).then(async function (decrypted) {
              let signingKey;
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              signingKey = await privateKey.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
              expect(await decrypted.signatures[1].verified).to.be.true;
              signingKey = await privKeyDE.getSigningKey();
              expect(decrypted.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[1].signature).packets.length).to.equal(1);
            });
          } finally {
            openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
          }
        });

        it('should fail to encrypt and decrypt/verify with detached signature as input for encryption with wrong public key', async function () {
          const signOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: privateKey,
            detached: true
          };

          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          };

          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          };

          return openpgp.sign(signOpt).then(async function (armoredSignature) {
            encOpt.signature = await openpgp.readSignature({ armoredSignature });
            return openpgp.encrypt(encOpt);
          }).then(async function (armoredMessage) {
            decOpt.message = await openpgp.readMessage({ armoredMessage });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal(plaintext);
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should fail to verify decrypted data with wrong public pgp key', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal(plaintext);
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should fail to verify decrypted null string with wrong public pgp key', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: '' }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal('');
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should successfully decrypt signed message without public keys to verify', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey,
            signingKeys: privateKey
          };
          const decOpt = {
            decryptionKeys: privateKey
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(async function ({ signatures, data }) {
            expect(data).to.equal(plaintext);
            await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
            const signingKey = await privateKey.getSigningKey();
            expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await signatures[0].signature).packets.length).to.equal(1);
          });
        });

        it('should fail to verify decrypted data with wrong public pgp key with detached signatures', async function () {
          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: publicKey
          });
          const signed = await openpgp.sign({
            message: await openpgp.createMessage({ text: plaintext }),
            signingKeys: privateKey,
            detached: true
          });
          const { signatures, data } = await openpgp.decrypt({
            message: await openpgp.readMessage({ armoredMessage: encrypted }),
            signature: await openpgp.readSignature({ armoredSignature: signed }),
            decryptionKeys: privateKey,
            verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
          });
          expect(data).to.equal(plaintext);
          await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
          const signingKey = await privateKey.getSigningKey();
          expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });

        it('should encrypt and decrypt/verify both signatures when signed with two private keys', async function () {
          const { rejectPublicKeyAlgorithms } = openpgp.config;
          try {
            openpgp.config.rejectPublicKeyAlgorithms = new Set();

            const privKeyDE = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
              passphrase
            });

            const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });

            const encOpt = {
              message: await openpgp.createMessage({ text: plaintext }),
              encryptionKeys: publicKey,
              signingKeys: [privateKey, privKeyDE]
            };

            const decOpt = {
              decryptionKeys: privateKey,
              verificationKeys: [publicKey, pubKeyDE]
            };

            await openpgp.encrypt(encOpt).then(async function (encrypted) {
              decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
              return openpgp.decrypt(decOpt);
            }).then(async function (decrypted) {
              let signingKey;
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              signingKey = await privateKey.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
              expect(await decrypted.signatures[1].verified).to.be.true;
              signingKey = await privKeyDE.getSigningKey();
              expect(decrypted.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[1].signature).packets.length).to.equal(1);
            });
          } finally {
            openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
          }
        });

        it('should fail to decrypt modified message', async function() {
          // need to generate new key with AEAD support
          const { privateKey } = await openpgp.generateKey({ userIDs: [{ email: 'test@email.com' }], type: 'rsa', format: 'object' });
          const { aeadAlgo } = await getPreferredCipherSuite([privateKey], undefined, undefined, openpgp.config);
          // sanity check
          expect(aeadAlgo).to.equal(openpgp.config.aeadProtect ? openpgp.config.preferredAEADAlgorithm : undefined);

          const encrypted = await openpgp.encrypt({
            message: await openpgp.createMessage({ binary: new Uint8Array(500) }),
            encryptionKeys: privateKey
          });
          // corrupt the SEIPD packet
          const encryptedCorrupted = encrypted.substr(0, 1000) + (encrypted[1000] === 'a' ? 'b' : 'a') + encrypted.substr(1001);

          const generateSingleChunkStream = () => (
            new ReadableStream({
              start(controller) {
                controller.enqueue(encryptedCorrupted);
                controller.close();
              }
            })
          );
          const generateMultiChunkStream = () => (
            new ReadableStream({
              start() {
                this.remaining = encryptedCorrupted.split('\n');
              },
              async pull(controller) {
                if (this.remaining.length) {
                  // sleep to slow down enqeueing
                  await new Promise(resolve => { setTimeout(resolve); });
                  controller.enqueue(this.remaining.shift() + '\n');
                } else {
                  controller.close();
                }
              }
            })
          );

          if (openpgp.config.aeadProtect) {
            const expectedError = /Authentication tag mismatch|Unsupported state or unable to authenticate data/;
            // AEAD fails either on AEAD chunk decryption or when reading the decrypted stream:
            // if the corruption is in the first AEAD chunk, then `openpgp.decrypt` will throw
            // when reading the decrypted stream to parse the packet list.
            await Promise.all([
              testStreamingDecryption(encryptedCorrupted, true, expectedError, true),
              testStreamingDecryption(encryptedCorrupted, false, expectedError, true),
              // `config.allowUnauthenticatedStream` does not apply to AEAD
              testStreamingDecryption(generateSingleChunkStream(), true, expectedError, openpgp.config.aeadChunkSizeByte > 0),
              testStreamingDecryption(generateSingleChunkStream(), false, expectedError, openpgp.config.aeadChunkSizeByte > 0),
              // Increasing number of streaming chunks should not affect the result
              testStreamingDecryption(generateMultiChunkStream(), true, expectedError, openpgp.config.aeadChunkSizeByte > 0),
              testStreamingDecryption(generateMultiChunkStream(), false, expectedError, openpgp.config.aeadChunkSizeByte > 0)
            ]);
          } else {
            const expectedError = /Modification detected/;
            await Promise.all([
              testStreamingDecryption(encryptedCorrupted, true, expectedError, true),
              testStreamingDecryption(encryptedCorrupted, false, expectedError, true),
              testStreamingDecryption(generateSingleChunkStream(), true, expectedError, false),
              testStreamingDecryption(generateSingleChunkStream(), false, expectedError, true),
              // Increasing number of streaming chunks should not affect the result
              testStreamingDecryption(generateMultiChunkStream(), true, expectedError, false),
              testStreamingDecryption(generateMultiChunkStream(), false, expectedError, true)
            ]);
          }

          async function testStreamingDecryption(encryptedDataOrStream, allowUnauthenticatedStream, expectedErrorMessage, expectedFailureOnDecrypt = null) {
            // parsing the message won't fail since armor checksum is ignored
            const message = await openpgp.readMessage({ armoredMessage: encryptedDataOrStream });
            let didFailOnDecrypt = true;

            try {
              const { data: decrypted } = await openpgp.decrypt({
                message,
                decryptionKeys: [privateKey],
                config: { allowUnauthenticatedStream }
              });
              didFailOnDecrypt = false;
              await stream.readToEnd(decrypted);
              // expected to have thrown
              throw new Error(`Expected decryption to fail with error ${expectedErrorMessage}`);
            } catch (e) {
              expect(e.message).to.match(expectedErrorMessage);
              expect(didFailOnDecrypt).to.equal(expectedFailureOnDecrypt);
            }
          }
        });

        it('should fail to decrypt unarmored message with garbage data appended', async function() {
          const key = privateKey;
          const message = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: key, signingKeys: key, format: 'binary' });
          const encrypted = util.concat([message, new Uint8Array([11])]);
          await expect((async () => {
            await openpgp.decrypt({ message: await openpgp.readMessage({ binaryMessage: encrypted }), decryptionKeys: key, verificationKeys: key });
          })()).to.be.rejectedWith('Error during parsing. This message / key probably does not conform to a valid OpenPGP format.');
        });
      });

      describe('ELG / DSA encrypt, decrypt, sign, verify', function() {

        it('round trip test', async function () {
          const { rejectPublicKeyAlgorithms } = openpgp.config;
          try {
            openpgp.config.rejectPublicKeyAlgorithms = new Set();

            const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
            const privKeyDE = await openpgp.decryptKey({
              privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
              passphrase
            });
            pubKeyDE.users[0].selfCertifications[0].features = [7]; // Monkey-patch AEAD feature flag
            await openpgp.encrypt({
              encryptionKeys: pubKeyDE,
              signingKeys: privKeyDE,
              message: await openpgp.createMessage({ text: plaintext })
            }).then(async function (encrypted) {
              return openpgp.decrypt({
                decryptionKeys: privKeyDE,
                verificationKeys: pubKeyDE,
                message: await openpgp.readMessage({ armoredMessage: encrypted })
              });
            }).then(async function (decrypted) {
              expect(decrypted.data).to.exist;
              expect(decrypted.data).to.equal(plaintext);
              expect(await decrypted.signatures[0].verified).to.be.true;
              const signingKey = await privKeyDE.getSigningKey();
              expect(decrypted.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
              expect((await decrypted.signatures[0].signature).packets.length).to.equal(1);
            });
          } finally {
            openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
          }
        });
      });

      describe('3DES decrypt', function() {
        const pgp_msg = [
          '-----BEGIN PGP MESSAGE-----',
          'Version: GnuPG/MacGPG2 v2.0.19 (Darwin)',
          'Comment: GPGTools - https://gpgtools.org',
          '',
          'hIwDBU4Dycfvp2EBA/9tuhQgOrcATcm2PRmIOcs6q947YhlsBTZZdVJDfVjkKlyM',
          'M0yE+lnNplWb041Cpfkkl6IvorKQd2iPbAkOL0IXwmVN41l+PvVgMcuFvvzetehG',
          'Ca0/VEYOaTZRNqyr9FIzcnVy1I/PaWT3iqVAYa+G8TEA5Dh9RLfsx8ZA9UNIaNI+',
          'ASm9aZ3H6FerNhm8RezDY5vRn6xw3o/wH5YEBvV2BEmmFKZ2BlqFQxqChr8UNwd1',
          'Ieebnq0HtBPE8YU/L0U=',
          '=JyIa',
          '-----END PGP MESSAGE-----'
        ].join('\n');

        const priv_key = [
          '-----BEGIN PGP PRIVATE KEY BLOCK-----',
          'Version: GnuPG/MacGPG2 v2.0.19 (Darwin)',
          'Comment: GPGTools - https://gpgtools.org',
          '',
          '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',
          '22q10ausyQXoOxXfDWVwKA==',
          '=IkKW',
          '-----END PGP PRIVATE KEY BLOCK-----'
        ].join('\n');

        it('Decrypt message', async function() {
          const privKey = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key }),
            passphrase: '1234'
          });
          const message = await openpgp.readMessage({ armoredMessage: pgp_msg });

          return openpgp.decrypt({ decryptionKeys:privKey, message:message }).then(function(decrypted) {
            expect(decrypted.data).to.equal('hello 3des\n');
            expect(decrypted.signatures.length).to.equal(0);
          });
        });
      });

      describe('AES encrypt, decrypt', function() {

        it('should encrypt and decrypt with one password', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: password1
          };
          const decOpt = {
            passwords: password1
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt and decrypt with two passwords', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: [password1, password2]
          };
          const decOpt = {
            passwords: password2
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt and decrypt with password and not ascii armor', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ text: plaintext }),
            passwords: password1,
            format: 'binary'
          };
          const decOpt = {
            passwords: password1
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.equal(plaintext);
            expect(decrypted.signatures.length).to.equal(0);
          });
        });

        it('should encrypt and decrypt with binary data', async function () {
          const encOpt = {
            message: await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]) }),
            passwords: password1,
            format: 'binary'
          };
          const decOpt = {
            passwords: password1,
            format: 'binary'
          };
          return openpgp.encrypt(encOpt).then(async function (encrypted) {
            decOpt.message = await openpgp.readMessage({ binaryMessage: encrypted });
            return openpgp.decrypt(decOpt);
          }).then(function (decrypted) {
            expect(decrypted.data).to.deep.equal(new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]));
            expect(decrypted.signatures.length).to.equal(0);
          });
        });
      });

      describe('Encrypt, decrypt with compression', function() {
        withCompression(function (modifyCompressionEncryptOptions, verifyCompressionDecrypted) {
          it('should encrypt and decrypt with one password', async function () {
            const encOpt = modifyCompressionEncryptOptions({
              message: await openpgp.createMessage({ text: plaintext }),
              passwords: password1
            });
            const decOpt = {
              passwords: password1
            };
            return openpgp.encrypt(encOpt).then(async function (encrypted) {
              decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
              return openpgp.decrypt(decOpt);
            }).then(function (decrypted) {
              expect(decrypted.data).to.equal(plaintext);
              expect(decrypted.signatures.length).to.equal(0);
              verifyCompressionDecrypted(decrypted);
            });
          });

          it('should encrypt and decrypt with one password (larger message)', async function () {
            const largerPlaintext = new Uint8Array(100_000);
            const encOpt = modifyCompressionEncryptOptions({
              message: await openpgp.createMessage({ binary: largerPlaintext }),
              passwords: password1
            });
            const decOpt = {
              passwords: password1,
              format: 'binary'
            };
            return openpgp.encrypt(encOpt).then(async function (encrypted) {
              decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
              return openpgp.decrypt(decOpt);
            }).then(function (decrypted) {
              expect(util.equalsUint8Array(decrypted.data, largerPlaintext)).to.be.true;
              expect(decrypted.signatures.length).to.equal(0);
              verifyCompressionDecrypted(decrypted);
            });
          });

          it('Streaming encrypt and decrypt small message roundtrip', async function() {
            const plaintext = [];
            let i = 0;
            const data = new globalThis.ReadableStream({
              pull(controller) {
                if (i++ < 4) {
                  const randomBytes = crypto.getRandomBytes(10);
                  controller.enqueue(randomBytes);
                  plaintext.push(randomBytes.slice());
                } else {
                  controller.close();
                }
              }
            });
            const encrypted = await openpgp.encrypt(modifyCompressionEncryptOptions({
              message: await openpgp.createMessage({ binary: data }),
              passwords: ['test']
            }));
            expect(stream.isStream(encrypted)).to.equal('web');

            const message = await openpgp.readMessage({ armoredMessage: encrypted });
            const decrypted = await openpgp.decrypt({
              passwords: ['test'],
              message,
              format: 'binary'
            });
            expect(stream.isStream(decrypted.data)).to.equal('web');
            expect(await stream.readToEnd(decrypted.data)).to.deep.equal(util.concatUint8Array(plaintext));
          });
        });
      });

    }

    describe('AES / RSA encrypt, decrypt, sign, verify', function() {
      const wrong_pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n' +
        'Version: OpenPGP.js v0.9.0\r\n' +
        'Comment: Hoodiecrow - https://hoodiecrow.com\r\n' +
        '\r\n' +
        'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5\r\n' +
        'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg\r\n' +
        'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM\r\n' +
        'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq\r\n' +
        'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1\r\n' +
        '=6XMW\r\n' +
        '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n';

      let decryptedPrivateKey; // to avoid decrypting key before each test
      beforeEach(async function() {
        if (!decryptedPrivateKey) {
          decryptedPrivateKey = await openpgp.decryptKey({ privateKey, passphrase });
        }
        privateKey = decryptedPrivateKey;
      });

      it('should sign and verify cleartext message', async function () {
        const message = await openpgp.createCleartextMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey
        };
        const verifyOpt = {
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/);
          verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify cleartext message with multiple private keys', async function () {
        const { rejectPublicKeyAlgorithms } = openpgp.config;
        try {
          openpgp.config.rejectPublicKeyAlgorithms = new Set();

          const privKeyDE = await openpgp.decryptKey({
            privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
            passphrase
          });

          const message = await openpgp.createCleartextMessage({ text: plaintext });
          const signOpt = {
            message,
            signingKeys: [privateKey, privKeyDE]
          };
          const verifyOpt = {
            verificationKeys: [publicKey, privKeyDE.toPublic()]
          };
          await openpgp.sign(signOpt).then(async function (signed) {
            expect(signed).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/);
            verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
            return openpgp.verify(verifyOpt);
          }).then(async function (verified) {
            let signingKey;
            expect(verified.data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
            expect(await verified.signatures[0].verified).to.be.true;
            signingKey = await privateKey.getSigningKey();
            expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await verified.signatures[0].signature).packets.length).to.equal(1);
            expect(await verified.signatures[1].verified).to.be.true;
            signingKey = await privKeyDE.getSigningKey();
            expect(verified.signatures[1].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
            expect((await verified.signatures[1].signature).packets.length).to.equal(1);
          });
        } finally {
          openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
        }
      });

      it('should sign and verify data with detached signatures', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          detached: true
        };
        const verifyOpt = {
          message,
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (armoredSignature) {
          verifyOpt.signature = await openpgp.readSignature({ armoredSignature });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext);
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and fail to verify cleartext message with wrong public pgp key', async function () {
        const message = await openpgp.createCleartextMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey
        };
        const verifyOpt = {
          verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function ({ data, signatures }) {
          expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
          await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
          const signingKey = await privateKey.getSigningKey();
          expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and fail to verify data with wrong public pgp key with detached signature', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          detached: true
        };
        const verifyOpt = {
          message,
          verificationKeys: await openpgp.readKey({ armoredKey: wrong_pubkey })
        };
        return openpgp.sign(signOpt).then(async function (armoredSignature) {
          verifyOpt.signature = await openpgp.readSignature({ armoredSignature });
          return openpgp.verify(verifyOpt);
        }).then(async function ({ data, signatures }) {
          expect(data).to.equal(plaintext);
          await expect(signatures[0].verified).to.be.rejectedWith(/Could not find signing key/);
          const signingKey = await privateKey.getSigningKey();
          expect(signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify data and not armor', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.message = await openpgp.readMessage({ binaryMessage: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext);
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify data and not armor with detached signatures', async function () {
        const start = util.normalizeDate();
        const message = await openpgp.createMessage({ text: plaintext });
        const signOpt = {
          message,
          signingKeys: privateKey,
          detached: true,
          format: 'binary'
        };
        const verifyOpt = {
          message,
          verificationKeys: publicKey
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(verified.data).to.equal(plaintext);
          expect(+(await verified.signatures[0].signature).packets[0].created).to.be.lte(+util.normalizeDate());
          expect(+(await verified.signatures[0].signature).packets[0].created).to.be.gte(+start);
          expect(await verified.signatures[0].verified).to.be.true;
          const signingKey = await privateKey.getSigningKey();
          expect(verified.signatures[0].keyID.toHex()).to.equal(signingKey.getKeyID().toHex());
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify data with a date in the past', async function () {
        const message = await openpgp.createMessage({ text: plaintext });
        const past = new Date(2000);
        const signOpt = {
          message,
          signingKeys: privateKey_1337,
          detached: true,
          date: past,
          format: 'binary',
          // SHA-512 cannot be used with a 512-bit RSA key (digest too long)
          config: { minRSABits: 512, preferredHashAlgorithm: openpgp.enums.hash.sha256 }
        };
        const verifyOpt = {
          message,
          verificationKeys: publicKey_1337,
          date: past
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed });
          return openpgp.verify(verifyOpt).then(async function (verified) {
            expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+past);
            expect(verified.data).to.equal(plaintext);
            expect(await verified.signatures[0].verified).to.be.true;
            expect(await privateKey_1337.getSigningKey(verified.signatures[0].keyID, past))
              .to.be.not.null;
            expect((await verified.signatures[0].signature).packets.length).to.equal(1);
            // now check with expiration checking disabled
            verifyOpt.date = null;
            return openpgp.verify(verifyOpt);
          }).then(async function (verified) {
            expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+past);
            expect(verified.data).to.equal(plaintext);
            expect(await verified.signatures[0].verified).to.be.true;
            expect(await privateKey_1337.getSigningKey(verified.signatures[0].keyID, null))
              .to.be.not.null;
            expect((await verified.signatures[0].signature).packets.length).to.equal(1);
          });
        });
      });

      it('should sign and verify binary data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const signOpt = {
          message: await openpgp.createMessage({ binary: data }),
          signingKeys: privateKey_2038_2045,
          detached: true,
          date: future,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey_2038_2045,
          date: future,
          format: 'binary'
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          verifyOpt.message = await openpgp.createMessage({ binary: data });
          verifyOpt.signature = await openpgp.readSignature({ binarySignature: signed });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(+(await verified.signatures[0].signature).packets[0].created).to.equal(+future);
          expect([].slice.call(verified.data)).to.deep.equal([].slice.call(data));
          expect(await verified.signatures[0].verified).to.be.true;
          expect(await privateKey_2038_2045.getSigningKey(verified.signatures[0].keyID, future))
            .to.be.not.null;
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign and verify binary data without one-pass signature', async function () {
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const signOpt = {
          message: await openpgp.createMessage({ binary: data }),
          signingKeys: privateKey,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey,
          format: 'binary'
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          const message = await openpgp.readMessage({ binaryMessage: signed });
          message.packets.push(...await stream.readToEnd(message.packets.stream, _ => _));
          const packets = new openpgp.PacketList();
          packets.push(message.packets.findPacket(openpgp.enums.packet.signature));
          packets.push(message.packets.findPacket(openpgp.enums.packet.literalData));
          verifyOpt.message = new openpgp.Message(packets);
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect([].slice.call(verified.data)).to.deep.equal([].slice.call(data));
          expect(await verified.signatures[0].verified).to.be.true;
          expect(await privateKey.getSigningKey(verified.signatures[0].keyID))
            .to.be.not.null;
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should streaming sign and verify binary data without one-pass signature', async function () {
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const dataStream = new globalThis.ReadableStream({
          start(controller) {
            controller.enqueue(data);
            controller.close();
          }
        });

        const signOpt = {
          message: await openpgp.createMessage({ binary: dataStream }),
          signingKeys: privateKey,
          format: 'binary'
        };
        const verifyOpt = {
          verificationKeys: publicKey,
          format: 'binary'
        };
        return openpgp.sign(signOpt).then(async function (signed) {
          expect(stream.isStream(signed)).to.equal('web');
          const message = await openpgp.readMessage({ binaryMessage: signed });
          message.packets.push(...await stream.readToEnd(message.packets.stream, _ => _));
          const packets = new openpgp.PacketList();
          packets.push(message.packets.findPacket(openpgp.enums.packet.signature));
          packets.push(message.packets.findPacket(openpgp.enums.packet.literalData));
          verifyOpt.message = await openpgp.readMessage({
            binaryMessage: stream.toStream(packets.write())
          });
          return openpgp.verify(verifyOpt);
        }).then(async function (verified) {
          expect(stream.isStream(verified.data)).to.equal('web');
          expect([].slice.call(await stream.readToEnd(verified.data))).to.deep.equal([].slice.call(data));
          expect(await verified.signatures[0].verified).to.be.true;
          expect(await privateKey.getSigningKey(verified.signatures[0].keyID))
            .to.be.not.null;
          expect((await verified.signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should encrypt and decrypt data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const encryptOpt = {
          message: await openpgp.createMessage({ text: plaintext, date: future }),
          encryptionKeys: publicKey_2038_2045,
          date: future,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config);
        }).then(async function (packets) {
          const literals = packets.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(+literals[0].date).to.equal(+future);
          expect(await stream.readToEnd(packets.getText())).to.equal(plaintext);
        });
      });

      it('should encrypt and decrypt binary data with a date in the past', async function () {
        const past = new Date(2005, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const encryptOpt = {
          message: await openpgp.createMessage({ binary: data, date: past }),
          encryptionKeys: publicKey_2000_2008,
          date: past,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2000_2008], undefined, undefined, undefined, openpgp.config);
        }).then(async function (packets) {
          const literals = packets.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(+literals[0].date).to.equal(+past);
          expect(await stream.readToEnd(packets.getLiteralData())).to.deep.equal(data);
        });
      });

      it('should sign, encrypt and decrypt, verify data with a date in the past', async function () {
        const past = new Date(2005, 5, 5, 5, 5, 5, 0);
        const encryptOpt = {
          message: await openpgp.createMessage({ text: plaintext, date: past }),
          encryptionKeys: publicKey_2000_2008,
          signingKeys: privateKey_2000_2008,
          date: past,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2000_2008], undefined, undefined, undefined, openpgp.config);
        }).then(async function (message) {
          const literals = message.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(+literals[0].date).to.equal(+past);
          const signatures = await message.verify([publicKey_2000_2008], past, undefined, openpgp.config);
          expect(await stream.readToEnd(message.getText())).to.equal(plaintext);
          expect(+(await signatures[0].signature).packets[0].created).to.equal(+past);
          expect(await signatures[0].verified).to.be.true;
          expect(await privateKey_2000_2008.getSigningKey(signatures[0].keyID, past))
            .to.be.not.null;
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign, encrypt and decrypt, verify binary data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const encryptOpt = {
          message: await openpgp.createMessage({ binary: data, date: future }),
          encryptionKeys: publicKey_2038_2045,
          signingKeys: privateKey_2038_2045,
          date: future,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config);
        }).then(async function (message) {
          const literals = message.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(literals[0].format).to.equal(openpgp.enums.literal.binary);
          expect(+literals[0].date).to.equal(+future);
          const signatures = await message.verify([publicKey_2038_2045], future, undefined, openpgp.config);
          expect(await stream.readToEnd(message.getLiteralData())).to.deep.equal(data);
          expect(+(await signatures[0].signature).packets[0].created).to.equal(+future);
          expect(await signatures[0].verified).to.be.true;
          expect(await privateKey_2038_2045.getSigningKey(signatures[0].keyID, future))
            .to.be.not.null;
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should sign, encrypt and decrypt, verify mime data with a date in the future', async function () {
        const future = new Date(2040, 5, 5, 5, 5, 5, 0);
        const data = new Uint8Array([3, 14, 15, 92, 65, 35, 59]);
        const encryptOpt = {
          message: await openpgp.createMessage({ binary: data, date: future, format: 'mime' }),
          encryptionKeys: publicKey_2038_2045,
          signingKeys: privateKey_2038_2045,
          date: future,
          format: 'binary'
        };

        return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
          const message = await openpgp.readMessage({ binaryMessage: encrypted });
          return message.decrypt([privateKey_2038_2045], undefined, undefined, undefined, openpgp.config);
        }).then(async function (message) {
          const literals = message.packets.filterByTag(openpgp.enums.packet.literalData);
          expect(literals.length).to.equal(1);
          expect(literals[0].format).to.equal(openpgp.enums.literal.mime);
          expect(+literals[0].date).to.equal(+future);
          const signatures = await message.verify([publicKey_2038_2045], future, undefined, openpgp.config);
          expect(await stream.readToEnd(message.getLiteralData())).to.deep.equal(data);
          expect(+(await signatures[0].signature).packets[0].created).to.equal(+future);
          expect(await signatures[0].verified).to.be.true;
          expect(await privateKey_2038_2045.getSigningKey(signatures[0].keyID, future))
            .to.be.not.null;
          expect((await signatures[0].signature).packets.length).to.equal(1);
        });
      });

      it('should fail to encrypt with revoked key', function() {
        return openpgp.revokeKey({
          key: privateKey,
          format: 'object'
        }).then(async function({ publicKey: revKey }) {
          return openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: revKey
          }).then(function() {
            throw new Error('Should not encrypt with revoked key');
          }).catch(function(error) {
            expect(error.message).to.match(/Primary key is revoked/);
          });
        });
      });

      it('should fail to encrypt with revoked subkey', async function() {
        const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
        const privKeyDE = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
          passphrase
        });
        return privKeyDE.subkeys[0].revoke(privKeyDE.keyPacket).then(async function(revSubkey) {
          pubKeyDE.subkeys[0] = revSubkey;
          return openpgp.encrypt({
            message: await openpgp.createMessage({ text: plaintext }),
            encryptionKeys: pubKeyDE,
            config: { rejectPublicKeyAlgorithms: new Set() }
          }).then(function() {
            throw new Error('Should not encrypt with revoked subkey');
          }).catch(function(error) {
            expect(error.message).to.match(/Could not find valid encryption key packet/);
          });
        });
      });

      it('should decrypt with revoked subkey', async function() {
        const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
        const privKeyDE = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
          passphrase
        });
        const encrypted = await openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: pubKeyDE,
          config: { rejectPublicKeyAlgorithms: new Set() }
        });
        privKeyDE.subkeys[0] = await privKeyDE.subkeys[0].revoke(privKeyDE.keyPacket);
        const decOpt = {
          message: await openpgp.readMessage({ armoredMessage: encrypted }),
          decryptionKeys: privKeyDE,
          config: { rejectPublicKeyAlgorithms: new Set() }
        };
        const decrypted = await openpgp.decrypt(decOpt);
        expect(decrypted.data).to.equal(plaintext);
      });

      it('should not decrypt with corrupted subkey', async function() {
        const pubKeyDE = await openpgp.readKey({ armoredKey: pub_key_de });
        const privKeyDE = await openpgp.readKey({ armoredKey: priv_key_de });
        // corrupt the public key params
        privKeyDE.subkeys[0].keyPacket.publicParams.p[0]++;
        // validation will check the primary key -- not the decryption subkey -- and will succeed (for now)
        const decryptedKeyDE = await openpgp.decryptKey({
          privateKey: privKeyDE,
          passphrase
        });
        const encrypted = await openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: pubKeyDE,
          config: { rejectPublicKeyAlgorithms: new Set() }
        });
        const decOpt = {
          message: await openpgp.readMessage({ armoredMessage: encrypted }),
          decryptionKeys: decryptedKeyDE
        };
        // binding signature is invalid
        await expect(openpgp.decrypt(decOpt)).to.be.rejectedWith(/Could not find valid subkey binding signature in key/);
      });

      it('RSA decryption with PKCS1 padding of wrong length should fail', async function() {
        const key = await openpgp.readKey({ armoredKey: rsaPrivateKeyPKCS1 });
        // the paddings of these messages are prefixed by 0x02 and 0x000002 instead of 0x0002
        // the code should discriminate between these cases by checking the length of the padded plaintext
        const padding02 = `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js VERSION
Comment: https://openpgpjs.org

wcBMAxbpoSTRSSl3AQf/fepDhqeam4Ecy8GUFChc47U3hbkdgINobI9TORAf
eGFZVcyTQKVIt7fB8bwQwjxRmU98xCjF7VkLhPQJkzKlkT9cIDBKswU+d3fw
lHAVYo77yUkFkVLXrQTZj/OjsA12V7lfRagO375XB3EpJUHVPvYQFFr3aSlo
FbsCrpZoS6FXxRYVjGpIeMjam3a7qDavQpKhjOQ+Sfm0tk2JZkQwpFom6x7c
9TEn3YSo6+I0ztjiuTBZDyYr8zocHW8imFzZRlcNuuuukesyFzFgHx46eVpO
6PVjmiN50agZvsV9rgPyyH84nb3zYJ63shnrQWubTOVH4daGbe8uHi+ZM3UU
J9I8AcH94nE77JUtCm7s1kOlo0EIshZsAqJwGveDGdAuabfViVwVxG4I24M6
8sqJYJd9FpNjSbYlrLT0R9zy
=+n/4
-----END PGP MESSAGE-----`;
        const padding000002 = `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js VERSION
Comment: https://openpgpjs.org

wcBMAxbpoSTRSSl3AQf/fepDhqeam4Ecy8GUFChc47U3hbkdgINobI9TORAf
eGFZVcyTQKVIt7fB8bwQwjxRmU98xCjF7VkLhPQJkzKlkT9cIDBKswU+d3fw
lHAVYo77yUkFkVLXrQTZj/OjsA12V7lfRagO375XB3EpJUHVPvYQFFr3aSlo
FbsCrpZoS6FXxRYVjGpIeMjam3a7qDavQpKhjOQ+Sfm0tk2JZkQwpFom6x7c
9TEn3YSo6+I0ztjiuTBZDyYr8zocHW8imFzZRlcNuuuukesyFzFgHx46eVpO
6PVjmiN50agZvsV9rgPyyH84nb3zYJ63shnrQWubTOVH4daGbe8uHi+ZM3UU
J9I8AcH94nE77JUtCm7s1kOlo0EIshZsAqJwGveDGdAuabfViVwVxG4I24M6
8sqJYJd9FpNjSbYlrLT0R9zy
=+n/4
-----END PGP MESSAGE-----`;

        const decOpt02 = {
          message: await openpgp.readMessage({ armoredMessage: padding02 }),
          decryptionKeys: key
        };
        await expect(openpgp.decrypt(decOpt02)).to.be.rejectedWith(/Decryption error/);

        const decOpt000002 = {
          message: await openpgp.readMessage({ armoredMessage: padding000002 }),
          decryptionKeys: key
        };
        await expect(openpgp.decrypt(decOpt000002)).to.be.rejectedWith(/Decryption error/);
      });

      it('should decrypt with two passwords message which GPG fails on', async function() {
        const decOpt = {
          message: await openpgp.readMessage({ armoredMessage: twoPasswordGPGFail }),
          passwords: password2
        };
        return openpgp.decrypt(decOpt).then(function(decrypted) {
          expect(decrypted.data).to.equal('short message\nnext line\n한국어/조선말');
          expect(decrypted.signatures.length).to.equal(0);
        });
      });

      it('should decrypt with three passwords', async function() {
        const messageBinary = util.hexToUint8Array('c32e04090308125231fe38b0255f60a7f319fc4959c147c7af33817ceb4cf159a00f2efa17b7921961f6ead025c77588d2430166fe9395cd58e9b69a67a30470e2d31bf0bbbb31c7eca31fb9015dddf70c6957036b093d104cbf0b26e218113e69c4fa89dda97a61d0cba364efa77d5144c5b9b701');
        const message = await openpgp.readMessage({ binaryMessage: messageBinary });
        const passwords = ['Test', 'Pinata', 'a'];
        const decrypted = await openpgp.decrypt({ message, passwords });
        expect(decrypted.data).to.equal('Hello world');
      });

      it('should decrypt broken ECC message from old OpenPGP.js', async function() {
        const key = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: ecdh_dec_key }),
          passphrase: '12345'
        });
        const message = await openpgp.readMessage({ armoredMessage: ecdh_msg_bad });
        const decrypted = await openpgp.decrypt({ message, decryptionKeys: key });
        expect(decrypted.data).to.equal('\n');
      });

      it('should decrypt broken ECC message from old go crypto', async function() {
        const key = await openpgp.decryptKey({
          privateKey: await openpgp.readKey({ armoredKey: ecdh_dec_key2 }),
          passphrase: '12345'
        });
        const message = await openpgp.readMessage({ armoredMessage: ecdh_msg_bad_2 });
        const decrypted = await openpgp.decrypt({ message, decryptionKeys: key });
        expect(decrypted.data).to.equal('Tesssst<br><br><br>Sent from ProtonMail mobile<br><br><br>');
      });

      it('should decrypt Blowfish message', async function() {
        const { data } = await openpgp.decrypt({
          passwords: 'test',
          message: await openpgp.readMessage({
            armoredMessage: `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v4.9.0
Comment: https://openpgpjs.org

wx4EBAMI7Di70u7hoDfgBUJQ2+1ig6ym3KMjRS9kAovSPAGRQLIPv2DgkINL
3DUgMNqtQCA23xWhq7Ly6o9H1lRfoAo7V5UElVCqGEX7cgyZjI97alY6Je3o
amnR6g==
=rPIK
-----END PGP MESSAGE-----`
          })
        });
        expect(data).to.equal('Hello World!');
      });

      it('should normalize newlines in encrypted text message', async function() {
        const message = await openpgp.createMessage({ text: '"BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\r\nUID:123\r\nDTSTART:20191211T121212Z\r\nDTEND:20191212T121212Z\r\nEND:VEVENT\nEND:VCALENDAR"' });
        const encrypted = await openpgp.encrypt({
          passwords: 'test',
          message
        });
        const decrypted = await openpgp.decrypt({
          passwords: 'test',
          message: await openpgp.readMessage({ armoredMessage: encrypted }),
          format: 'binary'
        });
        expect(util.decodeUTF8(decrypted.data)).to.equal('"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nBEGIN:VEVENT\r\nUID:123\r\nDTSTART:20191211T121212Z\r\nDTEND:20191212T121212Z\r\nEND:VEVENT\r\nEND:VCALENDAR"');
      });
    });

    it('should fail to decrypt a message containing a literal packet (and no session key)', async function() {
      const message = await openpgp.createMessage({ text: 'plaintext' });
      await expect(openpgp.decrypt({ message, passwords: 'password' })).to.be.rejectedWith(/Error decrypting message/);
    });

    it('should fail to decrypt a message containing a literal packet (and a session key)', async function() {
      const skeskPlusLiteralData = `-----BEGIN PGP MESSAGE-----

wy4ECQMIjvrInhvTxJwAbkqXp+KWFdBcjoPn03jCdyspVi9qXBDbyGaP1lrM
habAyxd1AGKaNp1wbGFpbnRleHQgbWVzc2FnZQ==
=XoUx
-----END PGP MESSAGE-----
      `;

      const message = await openpgp.readMessage({ armoredMessage: skeskPlusLiteralData, config: { enforceGrammar: false } });
      await expect(openpgp.decrypt({ message, passwords: 'password' })).to.be.rejectedWith(/No encrypted data found/);
    });

    it('should fail to decrypt non-integrity-protected message by default', async function() {
      const key = await openpgp.readKey({
        armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX
ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+
zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB
BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7
IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG
2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv
wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32
lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh
BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC
ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI
Bw==
=3Fja
-----END PGP PRIVATE KEY BLOCK-----`
      });
      const message = await openpgp.readMessage({
        armoredMessage: `-----BEGIN PGP MESSAGE-----

wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC
/RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57
OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM
kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq
rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu
bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ==
=T4iR
-----END PGP MESSAGE-----`
      });
      await expect(
        openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key })
      ).to.be.rejectedWith('Error decrypting message: Message is not authenticated.');
    });

    it('should allow decrypting non-integrity-protected message when enabled', async function() {
      const key = await openpgp.readKey({
        armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX
ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+
zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB
BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7
IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG
2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv
wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32
lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh
BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC
ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI
Bw==
=3Fja
-----END PGP PRIVATE KEY BLOCK-----`
      });
      const message = await openpgp.readMessage({
        armoredMessage: `-----BEGIN PGP MESSAGE-----

wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC
/RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57
OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM
kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq
rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu
bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ==
=T4iR
-----END PGP MESSAGE-----`
      });
      const decrypted = await openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key, config: { allowUnauthenticatedMessages: true } });
      expect(decrypted.data).to.equal('test');
    });

    it('should allow stream-decrypting non-integrity-protected message when enabled', async function() {
      const key = await openpgp.readKey({
        armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYD9r8xYJKwYBBAHaRw8BAQdApTaQJ6R/uooTqAuscoxYwbLrtoKndnsX
ydhqMybJqh0AAQCxNwi9Pezy03OQE0XOooBWaHiuhBtKA1eAuqjJFuuLuQ/+
zQDCjAQQFgoAHQUCYD9r8wQLCQcIAxUICgQWAgEAAhkBAhsDAh4BACEJEIkB
BTiDwpvwFiEEvRnmOby6fJ/OxUhSiQEFOIPCm/BidgEAq05ZiPseRsMTxNm7
IFQwQjmIFiWgLeQ0gKIvfl3SjBAA/iSPyTgWxSY98utXNuq+WoxVOzx3dJwG
2cflR/UFUlEPx10EYD9r8xIKKwYBBAGXVQEFAQEHQCASw+tMPvnXi904WASv
wRDUQofh0M7CpgQFqoOXvGlLAwEIBwAA/3gEimwdIet0gXb/hRRyBqOlcq32
lNREh+n+vZKJyXWYEjrCeAQYFggACQUCYD9r8wIbDAAhCRCJAQU4g8Kb8BYh
BL0Z5jm8unyfzsVIUokBBTiDwpvwHpEBAObHllPrJu0DqYyt4FKPkijgRpXC
ESqhlK5rrbc62SmfAQDVf5l1B6IDASBCKtC0VPPpYiK6AUcEISpaSXOa+pNI
Bw==
=3Fja
-----END PGP PRIVATE KEY BLOCK-----`
      });
      const message = await openpgp.readMessage({
        armoredMessage: stream.toStream(`-----BEGIN PGP MESSAGE-----

wV4D+3VwOibHmagSAQdATlMJlvrkaq46zMkbIuKBOJO5X3ugVwZpEyAterQC
/RUw0OPWeO+4swh/U7ZurV8cRr/fPnyGUUKI7rI+va3kWUZv4RRpUs7eYE57
OUr3yoMNyaQEBwu6VXiQrsBN8TyUbXQxb63p7EHFXIgvVDIvOG7bQptrrKlM
kKcB+fz5hb6mT/tl+cPcYHDOjocQ92pNVm+FilQhiATRxV8ah1DCOIZZ6tgq
rWwIiEQEBPt+tXOuVF4Peumovp3WgziudrJa5Jxt2Dz+8nicBglbZLXTsZNu
bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ==
=T4iR
-----END PGP MESSAGE-----`)
      });
      const decrypted = await openpgp.decrypt({ message, decryptionKeys: key, verificationKeys: key, config: { allowUnauthenticatedMessages: true } });
      const data = await stream.readToEnd(decrypted.data);
      expect(data).to.equal('test');
    });

    describe('X25519/Ed25519 (new format)', () => {
      it('should enforce using AES session keys with x25519 keys (v4 key)', async function () {
        // x25519 key (v4) with cast5 as preferred cipher
        const privateKeyCast5 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUkEZK8BixuMghYwdEgHl+3ASI4VZkn048KG4DVuugT1bMe4QTtFtQCoKBOG
JxrZh8E+7I5nK7McXP2U9gyC0+RFcD46AxSmRA46zQDCiAQQGwgAPgWCZK8B
iwQLAwcICZCaWrTxMIPhVwMVCAoEFgACAQIZAQKbAwIeARYhBDFBS8Xnfotk
Oun5WZpatPEwg+FXAABwwuNWCdr1WahiGrLupYaOYQO4S9y+FYTxqEV/gsOP
TKwmNIcIJPROV2LgyxvzQo79//0CocEYojEeUhGn7BH5lwvHSQRkrwGLGbVM
1JxFUJeQ253sHMko73uPkyyb9DvaeyWHPwgF2k9GACA9caoO8GsZI7KMnVGP
c4EpytBwVIsr4ck3QaEV/UxvDpnCdAQYGwgAKgWCZK8BiwmQmlq08TCD4VcC
mwwWIQQxQUvF536LZDrp+VmaWrTxMIPhVwAAXycLtMyiv0lon4qU5/rKWjrq
MIxMchUbHvktvUqomU0pDDLMPqLFtzBbtHqODPVbLTOygJRVLeHyWTOEfmOD
kl0L
=SYJZ
-----END PGP PRIVATE KEY BLOCK-----` });

        await expect(openpgp.generateSessionKey({
          encryptionKeys: privateKeyCast5,
          config: { preferredSymmetricAlgorithm: openpgp.enums.symmetric.cast5 }
        })).to.be.rejectedWith(/Could not generate a session key compatible with the given `encryptionKeys`/);

        await expect(openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: privateKeyCast5,
          sessionKey: { data: new Uint8Array(16).fill(1), algorithm: 'cast5' }
        })).to.be.rejectedWith(/X25519 and X448 keys can only encrypt AES session keys/);

        await expect(openpgp.decryptSessionKeys({
          message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----
  
wUQD66NYAXF0vfYZNWpc7s9eihtgj7EhHBeLOq2Ktw79artbhN5JMs+9aCIZ
A7sB7uYCTVCLIMfPFwVZH+c29gpCzPxSXQ==
=Dr02
-----END PGP MESSAGE-----` }),
          decryptionKeys: privateKeyCast5
        })).to.be.rejectedWith(/AES session key expected/);
      });

      it('supports decrypting new x25519 format (v4 key)', async function () {
        // v4 key
        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2
GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz
dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0
iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo
BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG
0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT
nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh
BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH
yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs=
=bJqd
-----END PGP PRIVATE KEY BLOCK-----` });

        const messageToDecrypt = `-----BEGIN PGP MESSAGE-----

wUQDYc6clYlCdtoZ3rAsvBDIwvoLmvM0zwViG8Ec0PgFfN5R6C4BqEZD53UZB1WM
J68hXSj1Sa235XAUYE1pZerTKhglvdI9Aeve8+L0w5RDMjmBBA50Yv/YT8liqhNi
mNwbfFbSNhZYWjFada77EKBn60j8QT/xCQzLR1clci7ieW2knw==
=NKye
-----END PGP MESSAGE-----`;
        const { data } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage: messageToDecrypt }),
          decryptionKeys: privateKey
        });
        expect(data).to.equal('Hello World!');
      });

      it('supports encrypting/decrypting new x25519 format (v4 key)', async function () {
        // v4 key
        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUkEZIbSkxsHknQrXGfb+kM2iOsOvin8yE05ff5hF8KE6k+saspAZQCy/kfFUYc2
GkpOHc42BI+MsysKzk4ofjBAfqM+bb7goQ3hzRV1c2VyIDx1c2VyQHRlc3QudGVz
dD7ChwQTGwgAPQUCZIbSkwmQQezK2iB2tIkWIQRqZza9wQZcwxpjGYNB7MraIHa0
iQIbAwIeAQIZAQILBwIVCAIWAAMnBwIAAFOeZ7jrKZsCzRfu1ffFa77074st0zRo
BTJXoXBQ1ZzLjsh+ZO6fB2odnYJtQYstv45H/3JyLVogcMnFeYmHeSP3AMdJBGSG
0pMZfpd7TiOQv7uKSK+k4HT9lKr5+dmvb7vox/8ids6unEkAF1v8fCKogIrtBWVT
nVbwnovjM3LLexpXFZSgTKRcNMgPRMJ0BBgbCAAqBQJkhtKTCZBB7MraIHa0iRYh
BGpnNr3BBlzDGmMZg0HsytogdrSJAhsMAADCYs2I9wBakIu9Hhxs4R3Jq9F8J7AH
yxsNL0GomZ+hxiE0MOZwRr10DxfVaRabF1fcf9PHSHX2SwEFXUKMIHgbMQs=
=bJqd
-----END PGP PRIVATE KEY BLOCK-----` });
        const plaintext = 'plaintext';

        const signed = await openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: privateKey
        });

        const { data } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage: signed }),
          decryptionKeys: privateKey
        });
        expect(data).to.equal(plaintext);
      });

      it('should decrypt test vector X25519-AEAD-OCB (PKESK v6, SEIPD v2)', async function() {
        // test vector https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.8
        const armoredMessage = `-----BEGIN PGP MESSAGE-----

wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
bhF30A+IitsxxA==
-----END PGP MESSAGE-----`;

        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });

        const { data: decryptedData } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage }),
          decryptionKeys: privateKey
        });

        expect(decryptedData).to.equal('Hello, world!');
      });
    });

    describe('X448/Ed448', () => {
      it('should enforce using AES session keys with x448 keys (v4 key)', async function () {
        // X448 key (v4) with cast5 as preferred cipher
        const privateKeyCast5 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xXsEZRrtaRyScvyNjK0o5ccICztnWhA1MSij7WdzPfuNy7ryUzB+kqzpziBR
IIKp5PN0NW3mOYRDnUyo7QHBl4AA30tR5ED8u5v/rNIzKz/mKsD6XeYy+d0Q
5utwuR8BUxx9mcIUGdS65z9H6PUMGnfCwqAGVCTzBrSCHgTNAMK3BBAcCgA7
BYJlGu1pAwsDBwmQkFi4G9HqQDwDFQgKAhYAAhkBApsDAh4BFiEE7kZAI1Dd
SVlLtf4QkFi4G9HqQDwAAPA7E+p0vwVLtUCfT0aBFzapFn8xjoow6jrUNTo3
8EtaN0fqP2vaeQwW/vv26wobD+hbL2RwyFtAEV6AeeDsPVhbx7WA7yKHPzvl
GOYEGw0h57DuhvSxGciuyt0Y5PR2Vrz/2/wHGcEHzsrhTNysUetluxEAx3kE
ZRrtaRrySCLAqKQSATJOXdoRoNKVasJHlKrG3qgMbt1U6uSdctHBitTiHHTf
GU/Jg0ADA3Eg0bCyDupWNACmHJGu7q0o7O7BTAm0AsMbHxoIkNN9JsijwAp5
FLtdXK9cAOkNaXPMkEGQkt1hmoW50lUq0iWcGBpzwqYEGBwKACoFgmUa7WkJ
kJBYuBvR6kA8ApsMFiEE7kZAI1DdSVlLtf4QkFi4G9HqQDwAAD3uf3qdwHY8
65W22GR17PbqF+9uvkPpXLBi32FVPFkxJqYvIN5/LAQ33xdEE0mzO4As4+Oi
x8fsFb2AEXLEwlSnL+Eo0O+iUQd3/94yMbMFRlNxrdaqZ3+7CehbnieI/vby
LIEnN38XBi0HE70uoU5prxUA
-----END PGP PRIVATE KEY BLOCK-----` });

        await expect(openpgp.generateSessionKey({
          encryptionKeys: privateKeyCast5,
          config: { preferredSymmetricAlgorithm: openpgp.enums.symmetric.cast5 }
        })).to.be.rejectedWith(/Could not generate a session key compatible with the given `encryptionKeys`/);

        await expect(openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: privateKeyCast5,
          sessionKey: { data: new Uint8Array(16).fill(1), algorithm: 'cast5' }
        })).to.be.rejectedWith(/X25519 and X448 keys can only encrypt AES session keys/);

        await expect(openpgp.decryptSessionKeys({
          message: await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----
  
wVwD2k7TUuqJwZkaXvEGk7B3pklJ5uRcRdKwwDJ40yKT0m5ic1e/2F+Se3xQ
zDE+N2DZ0B37pu4NUzTGBRo0oLD9EwwZA9+oJpBBOOry3cGmBYWvQHbvBpNE
5X5l8A==
-----END PGP MESSAGE-----` }),
          decryptionKeys: privateKeyCast5
        })).to.be.rejectedWith(/AES session key expected/);
      });

      it('should enforce using 512-bit signature digest', async function () {
        // X448 key using sha256 for self signatures
        const privateKeySHA256 = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xXsEZCWHXBwwtqciq6ZFU13s+dyhkWR5tOEmF1oX8OiP1B5ypfqyGVM8DkQh
5eTIMwB1oqJCROANoyA0q2dSigAAbDA5xr74DeClPPXC4ZXJ9uzuJWKvQvE8
x3EflhgoQCGBM7JfvH5zwdrJvPt8RKDvm0QkZzhPvnFoHnzNBHRlc3TCugQQ
HAgAPgWCZCWHXAQLCQcICZDsN6h/ys3ppwMVCAoEFgACAQIZAQKbAwIeARYh
BOJyE9P2eIcU2N2Ne+w3qH/KzemnAAAh1hTFCcEU77bU3YelrJTCNIOQnvt7
Hs6yZz2053CQTOC+wHkUQLaYYBEXSNyLZxoyv+NuGTiwbuYtAOlbE2erM7Cx
8B2Qz7M29UkFLMBUfb+yi+gTYYUWCXVQ7Um7MGjjgUG8+9p452i6f28mhRD8
tTgNAMd5BGQlh1wavTIFgILtbzrqQCiwDGx0YcFNzu9+FZ8vK5Mmm7UEZj0a
y7FWQtZw8tTaU6mY+RrSa52RjzkGLtQAQO++tgYqc+BnCFdCZ3ZYPRvD3mof
ffoo3l4xmto+iyvJZbQ4wQPXttg7VjCpEfOsL9TW9Xs09aIbysKmBBgcCAAq
BYJkJYdcCZDsN6h/ys3ppwKbDBYhBOJyE9P2eIcU2N2Ne+w3qH/KzemnAAC0
6/eZhh/Oj2gRdab2JeFGWACGIRDKxPXsWRCXR4YrSxcvCKK6rOvsyxQsgIsJ
JyPYkRPfmbKcseUDAEkSBLAfeizDGh7ea0GOdIMhwE/CW4f/H8ULbwi36y13
x3oMNVaYsI9dZ588Gpi8XYy2jOtqIPQ1AA==
-----END PGP PRIVATE KEY BLOCK-----` });

        await expect(privateKeySHA256.getSigningKey()).to.be.rejectedWith(/Hash algorithm too weak for EdDSA/);
      });

      it('supports encrypting/decrypting with x448 (v4 key)', async function () {
        // v4 key
        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xXsEZRqJ5BwHESfKnw5YJly5WobjigVm0kKY84NxrP6JKeIvIWiFqqSlozGpKZyR
50YbVTHmxpUCuJ7YNwX0UoAAoSO8IXmMM/XMd4ph00ju+fbSHdtQfyNhfFTi3UoM
V5DiFT+uOYDP+zwAwLWCR86csxmCWn6O10DNHcDNF1VzZXJBIDxVc2VyQUB0ZXN0
LnRlc3Q+wroEExwKAD4FAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXA
ivFIz7kCGwMCHgkCGQECCwcDFQoIAhYAAycHAgAA21/PqAuGDL5+3qrf3YoVOP+5
0BoJ+ZMhzcgax+cQTyndmOZYBfOqV/SJ8mf6CRhbB76JhGIvmRMtyYDQgDMVvcoA
yojVNs6e/Jco16bVJxM85wKDXJuq6AhtPQ8w/0WaCJtEf1uxqeQPEbOM+KtT/xY2
KgDHeQRlGonkGuOtAhogSIU3z/+gFzF8U7JQe7QDRYr9VWfi2WXFFarzg/3DMRur
oIB7mqkaaSatrvVuud1ZmRCWAMM4f57dvSdCKsVqSe+tlS225OmdWmnGLqyErBb6
44E2oENhDUom9OUGUPm8dXUjQbrmw6ec9hNLHWXCpgQYHAoAKgUCZRqJ5AkQLyXA
ivFIz7kWIQQZHazkGPlZUM3Q/HovJcCK8UjPuQIbDAAAZka10c8KlmwftJuboIV5
DalGWrZhbywJpEZRfoikcebSYi5++w1SbpXZGu27sl+BznGyyyqAfxyJjoCZaqCs
ewbKh04DNAg4v4v0W0a8UvD3j/CuciEMXjK9nUErt91zEwxNZy43yrQY2aAayDs8
94FqMAA=
=GBh1
-----END PGP PRIVATE KEY BLOCK-----` });
        const plaintext = 'plaintext';

        const signed = await openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: privateKey
        });

        const { data } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage: signed }),
          decryptionKeys: privateKey
        });
        expect(data).to.equal(plaintext);
      });

      it('supports encrypting/decrypting with x448 (v6 key)', async function () {
        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xX0GZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD
Y1E04mBai0pCoDiFVokwsKt3F5sAAC8lDYfVP/p3atbXJDTJB2W9WmZxIS7pUGhS
bjlWpZB/OVTBsoIfP/2J+Hi4ESwBRfDUDgwK4aJVKsLAIAYfHAoAAAA/BQJlGoti
IqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwMCHgkCCwcDFQoI
AhYABScHAwcCAAAAAPiGIG2qmhCULQ/+H4rKV0XEM1x0uVY3l878Pa6ijZLouZU/
VRd5PnbGyLPL++q3LDViUUdZ1uusRc01f677Q6wpUU90k8MH/oULwI0+KPtqe1N4
6nr1NTERsAmAaPjUdf4ZUXX/GWiTd/AlsS5JqGnAQxKRJkzCJacOTOElRMjzGUX7
CGaAnhSC86YRZ68ocTPfZysAzRdVc2VyQiA8VXNlckJAdGVzdC50ZXN0PsLADQYT
HAoAAAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUC
GQEAAAAASKwgVzMoPb2Hbr3lbNI1CRWECokYLokL7F8MbYiMnlg+v6QXLdStvT13
ZjxdrWQAx3MbihSOUSXbdAys90yMOAdtognj+x418J/TaYFMtIGBHwoHv8gQVnx9
9ICv8ezx1T5VvGBYNuKZ5Ww0WPEpYMf1VA+Y9JxpohdcRenNBdSug4tLWla2y8NH
aO28Fltpb4AuGQDHewZlGotiGgAAADjdabr1ohAOnbSUUkVhtUM/LVdnYgDLhmaj
YZ1N7TWY0fqEpMk2LLo2165HOmhddRPeTB1TWbuwBwB8lKc3czFUzYcAgvZ08T5S
UUHjfIhjeJeY4yd0OZDfzPw1vbegCc7t94bT+XGoIQbC/Bl7HCyAiMLADQYYHAoA
AAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwwA
AAAAHh0gf2kdqLoXdFX+aNVORr5VCVgcm2gBw8l68lDJ1ftA71bMllFi6Q5sLPUr
6UCpJYYk3o3hYUYzIHmCIZGVYe1pxRgIUNqxydXEfzylJmN5NbiJNkjJizrI7oAR
1mIcEEb/hmRMOUs1V2mcGuoeALBI/r/SyqDE2GRjH6d6g1RS7ZARPPHlZlY4CTqC
4a7L+8odDwA=
=chx0
-----END PGP PRIVATE KEY BLOCK-----` });
        const plaintext = 'plaintext';

        const signed = await openpgp.encrypt({
          message: await openpgp.createMessage({ text: plaintext }),
          encryptionKeys: privateKey
        });

        const { data } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage: signed }),
          decryptionKeys: privateKey
        });
        expect(data).to.equal(plaintext);
      });

      it('decrypt/verify should succeed using X448/Ed448 (PKESK v4, SEIPD v2, GCM)', async function() {
        // data generated by gopenpgp
        const armoredMessage = `-----BEGIN PGP MESSAGE-----

wWkGFQQ70agVm6o5r3tEzY5mrYaOV8yHChpUetZ33zrKGtw1F4PeFrE4bYkcTMQM
IcGoXcZj/0GJDCkOLLleSwrvuAuUwZV11bHBZ6eNTyj+XxhLdVflV/zqPmBhTHY9
SMn0YYHwgiQFk6PSwGICBwMMRYkTpsy0dE4YKasf6b4Oh9cn6HYY5rjnrtvrwD+F
LrsELfudYwpwHBA5jnO11Hl5mUyXhhWSdPoLGdeiYP5R/vZjqoZr3P6FL4dCdVni
fGChUUSYmpO4HIFrRBt2gAxl+f0Q8GCOG8c7EQ7c5600kJOlHM7SuoLqsxd482V1
H/1Rxd3cPwTDfOjH26KuDv60p0XjdCGyQXcDQMCPV+ZTs0TQl4wTFogZGaRMd9GC
5D5t/guKzR+H1ipXSFjFdWWTEehx8m0RKKKT3Bl81awKZb8ulR6YKI5x39nwOySN
azDRR3gn9xlKjcpa83k5sSZbUTxC8lzTeuMP0PkDrU2IpZUZOlzOhGYOGrtTFATK
PSoZU33h2h3hJqiX9aKrnw==
=Is5l
-----END PGP MESSAGE-----`;

        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xXsEZRqJ5BxV/xdxR4KvPofk3EzKaIZqM0Wlw904q+2S6Z84OmXN6Q1xCYurcwN1
wLOlGJ/CO2QhByEdGlUldwAAIQSMnMITcmXQU3EWK5S81FS+u1IZFP55j51bA5mS
HZ/A7MOpyN40ybW8mhMIXXUYB7kC/bOTmwVHGt3NF1VzZXJCIDxVc2VyQkB0ZXN0
LnRlc3Q+wrwEExwKAEAFAmUaieQJEP3WralmOT0PFiEEq4zuQzIBSh+Nk6G9/dat
qWY5PQ8CGwMCHgkCGQECCwcDFQoIAhYABScHAwcCAADAvnnhLp0DJYk7E0GfksCg
pUnnCjEePMVvRPVY3dwr9wLpdL/7T70fz541XVE8giYiZD7eiKvfc/nMgOhu1eqK
uXGUtDGBeabitJcrbquy2Tp/ENuW6rRHP7sAbu0mj6XxYEeCzKjGRT5Iq25AMevm
yeoIAMd5BGUaieQaqj/dF+uZGt9QLuji2eOlDC0/quq/sAtdJTbI1xj04aF0X7kJ
lVhEKeWZeAEpD4rVOCsrhMvr21gAu/BaFVKGUOuf0+ZE5jGcFcBvEP7OGyO296ry
zV7ONWS/FuoZ/NZmgWo9m9ftPtwqKDsgOWxiIj4cesKmBBgcCgAqBQJlGonkCRD9
1q2pZjk9DxYhBKuM7kMyAUofjZOhvf3WralmOT0PAhsMAADWb+0aY+NblShwsym/
2geh6XaqQUCJgdRfEl8xYLau/o8QQAzRp0ZBA+KeK3uwhRW3RizuqIw5iribAK3+
30Si5nvv0TivalPK2C9yAqzh9rkNNUQa9b17IHYs/WwQrvP3F5EZ3V+StqdveAEo
FecSL/wTAA==
=FANS
-----END PGP PRIVATE KEY BLOCK-----` });

        const senderKey = await openpgp.readKey({
          armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----

xj8EZRqJ5BwHESfKnw5YJly5WobjigVm0kKY84NxrP6JKeIvIWiFqqSlozGpKZyR
50YbVTHmxpUCuJ7YNwX0UoDNF1VzZXJBIDxVc2VyQUB0ZXN0LnRlc3Q+wroEExwK
AD4FAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXAivFIz7kCGwMCHgkC
GQECCwcDFQoIAhYAAycHAgAA21/PqAuGDL5+3qrf3YoVOP+50BoJ+ZMhzcgax+cQ
TyndmOZYBfOqV/SJ8mf6CRhbB76JhGIvmRMtyYDQgDMVvcoAyojVNs6e/Jco16bV
JxM85wKDXJuq6AhtPQ8w/0WaCJtEf1uxqeQPEbOM+KtT/xY2KgDOPgRlGonkGuOt
AhogSIU3z/+gFzF8U7JQe7QDRYr9VWfi2WXFFarzg/3DMRuroIB7mqkaaSatrvVu
ud1ZmRCWwqYEGBwKACoFAmUaieQJEC8lwIrxSM+5FiEEGR2s5Bj5WVDN0Px6LyXA
ivFIz7kCGwwAAGZGtdHPCpZsH7Sbm6CFeQ2pRlq2YW8sCaRGUX6IpHHm0mIufvsN
Um6V2Rrtu7Jfgc5xsssqgH8ciY6AmWqgrHsGyodOAzQIOL+L9FtGvFLw94/wrnIh
DF4yvZ1BK7fdcxMMTWcuN8q0GNmgGsg7PPeBajAA
=VA/P
-----END PGP PUBLIC KEY BLOCK-----` });

        const { data: decryptedData, signatures } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage }),
          decryptionKeys: privateKey,
          verificationKeys: senderKey
        });

        expect(decryptedData).to.equal('Hello there');
        expect(signatures).to.have.length(1);
        expect(await signatures[0].verified).to.be.true;
      });


      it('decrypt/verify should succeed using X448/Ed448 (PKESK v6, SEIPD v2, GCM)', async function() {
        // data generated by gopenpgp
        const armoredMessage = `-----BEGIN PGP MESSAGE-----

wXUGIQZh4qTsn8glFgGNbIdCTl8gH2OtkI/PAGCQ0gi9s9k/rhrDhXo7kUKDJ39F
fNp3kmAaM24Ce3bcYXwLy0gF2i6rxfL20D+g3cxv0i3CuXQCgcbojTN/8KY8ExiV
Xdfo+OWIZ5XndtyMpJW28BiLHru+n9bSwM8CBwMMENh7cT8lILXteh885FrUUD1Q
JMtD7xJUn2y78cVGgFSIkLbvFPDerB37xuhtMRkykuWgbUoJH/kcgBPdeCoYzJmf
LV9FyATv0/AYq0yWpQ0VUfNLTFyeHIGxz7NHvrzJSrOy1Gm31PXqWvb4sBROjnOX
oAk12JdPudz3l1QZT/DX947f4h6hwkVv7RRT0oOS2pMaz/mekRuD6utUcpsjFQ/M
EDphnhOsB4RH0il8YPVc9DCnf3GhSs66h+Z699MXHBaUmdtiN1IgoEgLfb/900U2
TfI6dvrvC56WIMA8EA1COvLGc9Ge4owW0UE8jIuqWLzA2nVg5belbzhNnOEh9b1c
OcDUh8CfBuXqHEi/ANMUOMmaIGfcHfQFVu5v/UMcLxcH/fSVF6DvtOxEoUxASWBS
mp6yC4A778BFuDFXb+/T8FjuJBaUj9rCSkYqt1TYVKG1XZPI4OdIvGtneo+vH/Cq
F6bxlLWU6oskZ5SE+xJblmmO01ObM9JRi9D8jZnXedTHExAnXHXIb8I=
=5RQw
-----END PGP MESSAGE-----`;

        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xX0GZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD
Y1E04mBai0pCoDiFVokwsKt3F5sAAC8lDYfVP/p3atbXJDTJB2W9WmZxIS7pUGhS
bjlWpZB/OVTBsoIfP/2J+Hi4ESwBRfDUDgwK4aJVKsLAIAYfHAoAAAA/BQJlGoti
IqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwMCHgkCCwcDFQoI
AhYABScHAwcCAAAAAPiGIG2qmhCULQ/+H4rKV0XEM1x0uVY3l878Pa6ijZLouZU/
VRd5PnbGyLPL++q3LDViUUdZ1uusRc01f677Q6wpUU90k8MH/oULwI0+KPtqe1N4
6nr1NTERsAmAaPjUdf4ZUXX/GWiTd/AlsS5JqGnAQxKRJkzCJacOTOElRMjzGUX7
CGaAnhSC86YRZ68ocTPfZysAzRdVc2VyQiA8VXNlckJAdGVzdC50ZXN0PsLADQYT
HAoAAAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUC
GQEAAAAASKwgVzMoPb2Hbr3lbNI1CRWECokYLokL7F8MbYiMnlg+v6QXLdStvT13
ZjxdrWQAx3MbihSOUSXbdAys90yMOAdtognj+x418J/TaYFMtIGBHwoHv8gQVnx9
9ICv8ezx1T5VvGBYNuKZ5Ww0WPEpYMf1VA+Y9JxpohdcRenNBdSug4tLWla2y8NH
aO28Fltpb4AuGQDHewZlGotiGgAAADjdabr1ohAOnbSUUkVhtUM/LVdnYgDLhmaj
YZ1N7TWY0fqEpMk2LLo2165HOmhddRPeTB1TWbuwBwB8lKc3czFUzYcAgvZ08T5S
UUHjfIhjeJeY4yd0OZDfzPw1vbegCc7t94bT+XGoIQbC/Bl7HCyAiMLADQYYHAoA
AAAsBQJlGotiIqEGobsxt8WKsMuJWANyTXpWMdC1QN/7EyJClfcs+nBgqdUCGwwA
AAAAHh0gf2kdqLoXdFX+aNVORr5VCVgcm2gBw8l68lDJ1ftA71bMllFi6Q5sLPUr
6UCpJYYk3o3hYUYzIHmCIZGVYe1pxRgIUNqxydXEfzylJmN5NbiJNkjJizrI7oAR
1mIcEEb/hmRMOUs1V2mcGuoeALBI/r/SyqDE2GRjH6d6g1RS7ZARPPHlZlY4CTqC
4a7L+8odDwA=
=chx0
-----END PGP PRIVATE KEY BLOCK-----` });

        const senderKey = await openpgp.readKey({
          armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----

xkMGZRqLYhwAAAA5U/IaIOge/FoLzCetXKx029bdJHCz2hMFBRMuzq4msjaT+hLe
V6puyC/PeSEfaanqTuo31vvsti2AwsAeBh8cCgAAAD0FAmUai2IioQYDGJ2wdEcO
zIVPDVDs6gYQASdGfG2EozBUGqEgvaj4dQIbAwIeCQILBwMVCggCFgADJwcCAAAA
ADYqIL5j5+FD/jwKRP1atdNf1IKfe8fPjdZv74CSalYvUdCaskTdLiAaW17NkrYT
2i9qDPErFWsvXi4LqGzqQnQkiJZBJ4x57EJPL4Z2vqPTBvgWEU2egi7fK7YAGZmk
Vf/n/X3Vh5ZSvIoUMChRmYqBBNI7MkS/I7QAJHkvi9XcANx44B0bz+yqETz2tNJ6
8VeeDgkAzRdVc2VyQSA8VXNlckFAdGVzdC50ZXN0PsLADQYTHAoAAAAsBQJlGoti
IqEGAxidsHRHDsyFTw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGQEAAAAAXD8gfvYz
WLLMxaFuC3C/RJH9fG84hb9mtPgjH3bfqW+g4Ti1ov8PjoJtk6ObtUB45J9J3G3X
FIqegAtGwI1Dy1U+M9dyXOqvpHwxs8iAFbEpwxLZ5K1ikFsbmoCZz4rmN0DbFyX2
JbltaV5nUtNqHiUXqoKIPvch98ANe3PDyIAxNf7TAzk3W0lQQa+Cp7TSiFEqJADO
QgZlGotiGgAAADjKb5lwMEt0ubSvwydaAF89wsn6H8NJO7kox5ioWW2Grn88CUZD
YaRBZj3ZH8HMdaih5kN4hJAeCMLADQYYHAoAAAAsBQJlGotiIqEGAxidsHRHDsyF
Tw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGwwAAAAAFIQgGYYweuBej4XHAgZrcez8
8VoTbIZDjMv6Qbj9g6jjW16Fyp10DKda10FFmbY+YjbNvQNYksF9bN/KFSS/PTYt
AVaOZDfW4fiN5s1QaYmA/xCT/zLHEYGryYCJLoLd7KLw28LS1KAWrC9h5cY6+fZE
05cavO/D/WqBLVPuA+5bftXnDvGcVS1p7buaMtQjKz4hAwA=
=GUIG
-----END PGP PUBLIC KEY BLOCK-----` });

        const { data: decryptedData, signatures } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage }),
          decryptionKeys: privateKey,
          verificationKeys: senderKey
        });

        expect(decryptedData).to.equal('Hello there');
        expect(signatures).to.have.length(1);
        expect(await signatures[0].verified).to.be.true;
      });

      it('decrypt/verify should succeed using X448/Ed448 (PKESK v6, SEIPD v2, OCB)', async function() {
        // data generated by gopenpgp
        const armoredMessage = `-----BEGIN PGP MESSAGE-----

wXUGIQaYemlYu2ObOZ2IjFbL77NygqexwaCgtb0COZ0EnXfXlBri0wADNxbvwCnJ
GDlRX9VhIy46oPAvVJjm2d7ZC6wqxNfFuzQEB8KzwYBkExmZuAfO5KJ8la6+DRhc
OUH3A9cBGzq0eiKaKRqjHkiLHY5pFNPSwNoCBwIM98RL63I8iMyxcXpXQlBrYlBx
5uegrENlleNg6UJFr7rBT4eJH+Qeksb//V87eZymzqXZBsrTYmUjsFgYd5kL8NlU
wovy+qQnZmEaUKieDx3w+orR8b32ub5CNjHJa5lCdNWsIK825S5JUifZDd3hR6lC
EgtZRwxY/1CyQU94LR9j6w/YVF0W31+LxGGkL+uJEx0khJUzpxUM9QSEREOY7Frs
EegHNwDvxvxEwWpfkJOPIDME6Y7UcpsNp6xgiZ/XF06IRsliCRbeYaH1IWW+y0OS
CmPvvTFUzjwTxWogDccHz8YLHU0y6TKxf14YMvVLg2tf2P/BVVZSg0ejz6pfDKA5
AP+Q/eXBAH272SpBjKo7YcVpTsz0KpWyhB6Jra4xaUFkt6pg39ydR3RJMvxbQVlR
aZqV/+1rwIiIauyHKiJFdCiXYPDU3xibVkFIFhuk5JwHm29XvOV1r8FFx7d78X5P
yJnXcXsl+GxwOojcLXSL0CEIU/iRqyAIyyhvUyyss3glehhgx0fENV2P/Ygi/naN
nJUJgg==
=m19C
-----END PGP MESSAGE-----`;

        const privateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xX0GZRqLYhwAAAA5U/IaIOge/FoLzCetXKx029bdJHCz2hMFBRMuzq4msjaT+hLe
V6puyC/PeSEfaanqTuo31vvsti2AAIttr4GDGXF4vfPzbzkWV9dT4VVsIU7QqLv1
hzwZ+k7pHroRyXnUiYxRYHuzlg7Vw4CrAtN/8T65OMLAHgYfHAoAAAA9BQJlGoti
IqEGAxidsHRHDsyFTw1Q7OoGEAEnRnxthKMwVBqhIL2o+HUCGwMCHgkCCwcDFQoI
AhYAAycHAgAAAAA2KiC+Y+fhQ/48CkT9WrXTX9SCn3vHz43Wb++AkmpWL1HQmrJE
3S4gGltezZK2E9ovagzxKxVrL14uC6hs6kJ0JIiWQSeMeexCTy+Gdr6j0wb4FhFN
noIu3yu2ABmZpFX/5/191YeWUryKFDAoUZmKgQTSOzJEvyO0ACR5L4vV3ADceOAd
G8/sqhE89rTSevFXng4JAM0XVXNlckEgPFVzZXJBQHRlc3QudGVzdD7CwA0GExwK
AAAALAUCZRqLYiKhBgMYnbB0Rw7MhU8NUOzqBhABJ0Z8bYSjMFQaoSC9qPh1AhkB
AAAAAFw/IH72M1iyzMWhbgtwv0SR/XxvOIW/ZrT4Ix9236lvoOE4taL/D46CbZOj
m7VAeOSfSdxt1xSKnoALRsCNQ8tVPjPXclzqr6R8MbPIgBWxKcMS2eStYpBbG5qA
mc+K5jdA2xcl9iW5bWleZ1LTah4lF6qCiD73IffADXtzw8iAMTX+0wM5N1tJUEGv
gqe00ohRKiQAx3sGZRqLYhoAAAA4ym+ZcDBLdLm0r8MnWgBfPcLJ+h/DSTu5KMeY
qFlthq5/PAlGQ2GkQWY92R/BzHWooeZDeISQHggAuraV/u+CE642fcbcq90OY+qg
n739wkHcBps/s/MgMI+Q2H13vEsFpYZ/kuBIIYP39xkdU48/1GbCwA0GGBwKAAAA
LAUCZRqLYiKhBgMYnbB0Rw7MhU8NUOzqBhABJ0Z8bYSjMFQaoSC9qPh1AhsMAAAA
ABSEIBmGMHrgXo+FxwIGa3Hs/PFaE2yGQ4zL+kG4/YOo41tehcqddAynWtdBRZm2
PmI2zb0DWJLBfWzfyhUkvz02LQFWjmQ31uH4jebNUGmJgP8Qk/8yxxGBq8mAiS6C
3eyi8NvC0tSgFqwvYeXGOvn2RNOXGrzvw/1qgS1T7gPuW37V5w7xnFUtae27mjLU
Iys+IQMA
=iwhO
-----END PGP PRIVATE KEY BLOCK-----` });

        const senderKey = await openpgp.readKey({
          armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----

xkMGZRqLYhwAAAA52IEq/TpKiPp6RofQaq4uhCruTtiG+qiVFnwsQgeh0ui34kHD
Y1E04mBai0pCoDiFVokwsKt3F5sAwsAgBh8cCgAAAD8FAmUai2IioQahuzG3xYqw
y4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIbAwIeCQILBwMVCggCFgAFJwcDBwIA
AAAA+IYgbaqaEJQtD/4fispXRcQzXHS5VjeXzvw9rqKNkui5lT9VF3k+dsbIs8v7
6rcsNWJRR1nW66xFzTV/rvtDrClRT3STwwf+hQvAjT4o+2p7U3jqevU1MRGwCYBo
+NR1/hlRdf8ZaJN38CWxLkmoacBDEpEmTMIlpw5M4SVEyPMZRfsIZoCeFILzphFn
ryhxM99nKwDNF1VzZXJCIDxVc2VyQkB0ZXN0LnRlc3Q+wsANBhMcCgAAACwFAmUa
i2IioQahuzG3xYqwy4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIZAQAAAABIrCBX
Myg9vYduveVs0jUJFYQKiRguiQvsXwxtiIyeWD6/pBct1K29PXdmPF2tZADHcxuK
FI5RJdt0DKz3TIw4B22iCeP7HjXwn9NpgUy0gYEfCge/yBBWfH30gK/x7PHVPlW8
YFg24pnlbDRY8Slgx/VUD5j0nGmiF1xF6c0F1K6Di0taVrbLw0do7bwWW2lvgC4Z
AM5CBmUai2IaAAAAON1puvWiEA6dtJRSRWG1Qz8tV2diAMuGZqNhnU3tNZjR+oSk
yTYsujbXrkc6aF11E95MHVNZu7AHwsANBhgcCgAAACwFAmUai2IioQahuzG3xYqw
y4lYA3JNelYx0LVA3/sTIkKV9yz6cGCp1QIbDAAAAAAeHSB/aR2ouhd0Vf5o1U5G
vlUJWBybaAHDyXryUMnV+0DvVsyWUWLpDmws9SvpQKklhiTejeFhRjMgeYIhkZVh
7WnFGAhQ2rHJ1cR/PKUmY3k1uIk2SMmLOsjugBHWYhwQRv+GZEw5SzVXaZwa6h4A
sEj+v9LKoMTYZGMfp3qDVFLtkBE88eVmVjgJOoLhrsv7yh0PAA==
=2Usy
-----END PGP PUBLIC KEY BLOCK-----` });

        const { data: decryptedData, signatures } = await openpgp.decrypt({
          message: await openpgp.readMessage({ armoredMessage }),
          decryptionKeys: privateKey,
          verificationKeys: senderKey
        });

        expect(decryptedData).to.equal('Hello nice to meet you');
        expect(signatures).to.have.length(1);
        expect(await signatures[0].verified).to.be.true;
      });
    });

    describe('Sign and verify with each curve', function() {
      const curves = ['secp256k1' , 'nistP256', 'nistP384', 'nistP521', 'curve25519Legacy', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
      curves.forEach(curve => {
        it(`sign/verify with ${curve}`, async function() {
          const config = { rejectCurves: new Set() };
          const plaintext = 'short message';
          const { privateKey: key } = await openpgp.generateKey({ curve, userIDs: { name: 'Alice', email: 'info@alice.com' }, format: 'object', config });
          const signed = await openpgp.sign({ signingKeys:[key], message: await openpgp.createCleartextMessage({ text: plaintext }), config });
          const verified = await openpgp.verify({ verificationKeys:[key], message: await openpgp.readCleartextMessage({ cleartextMessage: signed }), config });
          expect(await verified.signatures[0].verified).to.be.true;
        });
      });

      it('sign/verify with new Ed25519 format', async function () {
        const userIDs = { name: 'Alice', email: 'info@alice.com' };
        const { privateKey } = await openpgp.generateKey({ type: 'curve25519', userIDs, format: 'object' });
        const plaintext = 'plaintext';

        const signed = await openpgp.sign({
          message: await openpgp.createMessage({ text: plaintext }),
          signingKeys: privateKey
        });

        const { signatures, data } = await openpgp.verify({
          message: await openpgp.readMessage({ armoredMessage: signed }),
          verificationKeys: privateKey
        });
        expect(data).to.equal(plaintext);
        expect(signatures).to.have.length(1);
        expect(await signatures[0].verified).to.be.true;
      });

      it('sign/verify with Ed448', async function () {
        const userIDs = { name: 'Alice', email: 'info@alice.com' };
        const { privateKey } = await openpgp.generateKey({ type: 'curve448', userIDs, format: 'object' });
        const plaintext = 'plaintext';

        const signed = await openpgp.sign({
          message: await openpgp.createMessage({ text: plaintext }),
          signingKeys: privateKey
        });

        const { signatures, data } = await openpgp.verify({
          message: await openpgp.readMessage({ armoredMessage: signed }),
          verificationKeys: privateKey
        });
        expect(data).to.equal(plaintext);
        expect(signatures).to.have.length(1);
        expect(await signatures[0].verified).to.be.true;
      });
    });

    describe('Errors', function() {

      it('Error message should contain the original error message', async function() {
        return openpgp.encrypt({
          message: await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01]) }),
          passwords: null
        }).then(function() {
          throw new Error('Error expected.');
        }).catch(function(error) {
          expect(error.message).to.match(/No keys, passwords, or session key provided/);
        });
      });

    });

    describe('Specific encryption/signing key testing', () => {
      const encryptionKeyIDs = [
        keyIDType.fromID('87EAE0977B2185EA'),
        keyIDType.fromID('F94F9B34AF93FA14'),
        keyIDType.fromID('08F7D4C7C59545C0')
      ];
      const signingKeyIDs = [
        keyIDType.fromID('663277AF60400638'),
        keyIDType.fromID('BBE14491E6EE6366'),
        keyIDType.fromID('3E0F20F1A71D6DFD')
      ];
      const getPrimaryKey = async () => openpgp.readKey({
        armoredKey: multipleEncryptionAndSigningSubkeys
      });

      it('Encrypt message with a specific encryption key id', async function () {
        const primaryKey = await getPrimaryKey();
        let m;
        let p;
        for (let i = 0; i < encryptionKeyIDs.length; i++) {
          m = await openpgp.readMessage({
            armoredMessage: await openpgp.encrypt({
              message: await openpgp.createMessage({ text: 'Hello World\n' }),
              encryptionKeys: primaryKey,
              encryptionKeyIDs: [encryptionKeyIDs[i]]
            })
          });
          p = m.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey);
          expect(p.length).equals(1);
          expect(p[0].publicKeyID.equals(encryptionKeyIDs[i])).to.be.true;
        }
      });

      it('Sign message with a specific signing key id', async function () {
        const primaryKey = await getPrimaryKey();
        let s;
        let p;
        for (let i = 0; i < signingKeyIDs.length; i++) {
          s = await openpgp.readSignature({
            armoredSignature: await openpgp.sign({
              message: await openpgp.createMessage({ text: 'Hello World\n' }),
              signingKeys: primaryKey,
              signingKeyIDs: [signingKeyIDs[i]],
              detached: true
            })
          });
          p = s.packets.filterByTag(openpgp.enums.packet.signature);
          expect(p.length).equals(1);
          expect(p[0].issuerKeyID.equals(signingKeyIDs[i])).to.be.true;
        }
      });

      it('Encrypt and sign with specific encryption/signing key ids', async function () {
        const primaryKey = await getPrimaryKey();
        const plaintextMessage = await openpgp.createMessage({ text: 'Hello World\n' });

        const checkEncryptedPackets = (encryptionKeyIDs, pKESKList) => {
          pKESKList.forEach(({ publicKeyID }, i) => {
            expect(publicKeyID.equals(encryptionKeyIDs[i])).to.be.true;
          });
        };
        const checkSignatures = (signingKeyIDs, signatures) => {
          signatures.forEach(({ keyID }, i) => {
            expect(keyID.equals(signingKeyIDs[i])).to.be.true;
          });
        };

        const kIds = [encryptionKeyIDs[1], encryptionKeyIDs[0], encryptionKeyIDs[2]];
        const sIds = [signingKeyIDs[2], signingKeyIDs[1], signingKeyIDs[0]];
        const message = await openpgp.readMessage({
          armoredMessage: await openpgp.encrypt({
            message: plaintextMessage,
            signingKeys: [primaryKey, primaryKey, primaryKey],
            encryptionKeys: [primaryKey, primaryKey, primaryKey],
            encryptionKeyIDs: kIds,
            signingKeyIDs: sIds
          })
        });
        const pKESKList = message.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey);
        expect(pKESKList.length).equals(3);
        checkEncryptedPackets(kIds, pKESKList);
        const { signatures } = await openpgp.decrypt({
          message,
          decryptionKeys: [primaryKey, primaryKey, primaryKey]
        });
        expect(signatures.length).equals(3);
        checkSignatures(sIds, signatures);
      });
    });
  });
});
